Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d61792433c | ||
|
|
6016efa436 | ||
|
|
ed428e87c4 | ||
|
|
69ecec7ecf | ||
|
|
a90f6eb357 | ||
|
|
c4642919f7 | ||
|
|
c4733e0624 | ||
|
|
e1faaf7a76 | ||
|
|
47f29f9fa9 | ||
|
|
88497826ec | ||
|
|
b319d1a5e1 | ||
|
|
13f3648e4d | ||
|
|
359fb922aa | ||
|
|
b6448c9876 | ||
|
|
15d54b16f6 | ||
|
|
96e4085919 | ||
|
|
6bcc351e07 |
@@ -8,29 +8,57 @@ git clone https://git.gbrown.org/gib/Panama.git ~/.local/share/Panama
|
||||
~/.local/share/Panama/install
|
||||
```
|
||||
|
||||
`install` runs everything in `setup/scripts/` in order:
|
||||
`install` asks its questions first and then runs the stages in `setup/scripts/`
|
||||
in order, without stopping again:
|
||||
|
||||
| Script | Does |
|
||||
|---|---|
|
||||
| `install-packages` | Repos (RPM Fusion, Terra, Hyprland COPR), then the four package lists in `setup/packages/` |
|
||||
| `link-dotfiles` | Symlinks `config/dot/<name>` → `~/.config/<name>` |
|
||||
| `interview` | Every prompt, before anything is installed. Answers last one run and are never written to a durable path |
|
||||
| `install-packages` | Repos (RPM Fusion, Terra, Hyprland COPR), the package lists in `setup/packages/`, then whichever optional categories were chosen |
|
||||
| `link-dotfiles` | Symlinks `config/dot/<name>` → `~/.config/<name>`, and seeds the wallpaper, cursor theme and Firefox chrome |
|
||||
| `change-settings` | Copies `config/copy/` over `/`, applies gsettings, enables user services |
|
||||
| `link-vicinae-scripts` | Publishes the Vicinae script commands |
|
||||
| `setup-identity` | git config, `gh auth login`, an SSH key — whichever were asked for |
|
||||
| `install-hardware` | NVIDIA, Secure Boot enrollment, Fedora's extras, firmware — each only if it was asked for. Last, because enrollment and firmware are consumed at the next boot |
|
||||
|
||||
The run ends with a health summary from `panama-doctor`, which reports what is
|
||||
actually running rather than what was attempted. It never fails the install: on a
|
||||
fresh machine it legitimately reports things as not yet configured.
|
||||
|
||||
### Optional applications
|
||||
|
||||
Every machine gets the lists in `setup/packages/`. The interview also offers the
|
||||
categories in `setup/packages/extras/` as a checklist, so a work laptop need not
|
||||
acquire emulators and a desktop need not skip Steam. Nothing is preselected.
|
||||
|
||||
A category is one file. A bare line is a dnf package and a `flatpak:` line is a
|
||||
Flathub ID, because the applications in a category do not all come from one
|
||||
place. Adding a category is adding a file — the menu is read from the directory,
|
||||
not written down anywhere.
|
||||
|
||||
Existing configs are moved to `config/old/` rather than overwritten.
|
||||
|
||||
## Desktops
|
||||
## The desktop
|
||||
|
||||
Panama configures **two** desktops that coexist. Both sessions stay available in
|
||||
GDM, so you can switch back and forth while you settle in.
|
||||
Hyprland, with a shell written from scratch. It began as a replacement for a
|
||||
GNOME session — Forge for tiling, Dash-to-Dock, Openbar, Vitals — and was built
|
||||
to reproduce it closely enough that muscle memory transferred: same keybinds,
|
||||
same panel contents, same dock, same Tokyo Night Moon palette.
|
||||
|
||||
### GNOME
|
||||
The original setup: Forge for tiling, Dash-to-Dock, Openbar, Vitals,
|
||||
AppIndicator support. Configured through `config/dot/forge/` and gsettings.
|
||||
That is history now rather than a second option. Panama installs and configures
|
||||
one desktop, and the GNOME session it grew out of is neither installed nor
|
||||
configured here. What each piece replaced is recorded in
|
||||
[`config/dot/hypr/DESKTOP-PARITY.md`](config/dot/hypr/DESKTOP-PARITY.md) and in
|
||||
the comments of the components themselves, because knowing what a thing was
|
||||
modelled on explains why it behaves the way it does.
|
||||
|
||||
### Hyprland
|
||||
A from-scratch replacement built to reproduce the GNOME setup closely enough
|
||||
that muscle memory transfers — same keybinds, same panel contents, same dock,
|
||||
same Tokyo Night Moon palette.
|
||||
GNOME is not gone from the machine: `gnome-control-center` is a declared
|
||||
dependency, and Panama's own Settings hands off to it for the panels it
|
||||
deliberately does not own — Online Accounts, Color, Sound, Network, Keyboard,
|
||||
Privacy, Wellbeing, Accessibility, and System for users, date and time, region
|
||||
and remote desktop. The allow-list in
|
||||
[`services/SystemSettings.qml`](config/dot/quickshell/services/SystemSettings.qml)
|
||||
is what decides; anything not on it is a panel Panama owns itself.
|
||||
|
||||
| Piece | What it is |
|
||||
|---|---|
|
||||
@@ -50,13 +78,56 @@ Log in as **"Hyprland (uwsm-managed)"**, not plain "Hyprland".
|
||||
## Layout
|
||||
|
||||
```
|
||||
bin/ Small user-facing commands on PATH
|
||||
bin/ Small user-facing commands on PATH; `panama` is the entry point
|
||||
config/
|
||||
bash/ .bashrc, aliases, env (env is gitignored)
|
||||
copy/ Files copied verbatim over / (needs sudo)
|
||||
dot/ Symlinked into ~/.config
|
||||
firefox/ Vendored Firefox chrome, linked into the browser profile
|
||||
local/ Icons and the cursor theme, linked into ~/.local/share
|
||||
old/ Backups of whatever was replaced (gitignored)
|
||||
wallpapers/ Copied into ~/Pictures/Wallpapers when absent
|
||||
setup/
|
||||
packages/ One package per line
|
||||
packages/ One package per line; extras/ holds the optional categories
|
||||
scripts/ Run in order by ./install
|
||||
tests/ Contracts. See below
|
||||
docs/ Settings reference, and the design specs behind the work
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
121 of them, under `tests/`. Run the lot, or a subset by pattern:
|
||||
|
||||
```sh
|
||||
panama test # everything
|
||||
panama test dock # just the ones matching "dock"
|
||||
tests/setup/interview-contract # or one directly; they are plain executables
|
||||
```
|
||||
|
||||
They are called contracts rather than unit tests because that is what they are:
|
||||
each one pins a decision that was expensive to get right and is cheap to undo by
|
||||
accident. Most read or measure the real thing — launching a shell to measure a
|
||||
surface's geometry, standing stub commands on `PATH` to see what a stage would
|
||||
have installed, running a script against a throwaway `HOME` — rather than
|
||||
asserting things about source text, because the bugs worth catching here have all
|
||||
been ones that source text looked fine for.
|
||||
|
||||
```
|
||||
tests/setup/ The installer: the interview, package lists, hardware, extras
|
||||
tests/quickshell/ The shell and its settings pages
|
||||
tests/hypr/ The compositor config
|
||||
```
|
||||
|
||||
## The `panama` command
|
||||
|
||||
```sh
|
||||
panama update # review, commit and sync this repo
|
||||
panama edit # open it in Neovim
|
||||
panama doctor # what is actually running, not what was installed
|
||||
panama test # every contract, or a subset by pattern
|
||||
panama upgrade # re-run ./install from anywhere
|
||||
```
|
||||
|
||||
None of the scripts in this repository carry a `.sh` extension. A shebang and
|
||||
the executable bit already select the interpreter, and the extension only
|
||||
becomes something to keep in sync — which it did not stay.
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
# Commands:
|
||||
# update Commit & sync local changes (or just pull if clean)
|
||||
# edit Open the Panama repo in Neovim
|
||||
# doctor Report what is actually running on this machine
|
||||
# test Run every contract under tests/
|
||||
# upgrade Re-run the installer from anywhere
|
||||
# help Show this help
|
||||
#
|
||||
# Designed to grow: add new subcommands as cmd_<name> functions and
|
||||
@@ -62,6 +65,12 @@ ${BOLD}Commands:${RESET}
|
||||
${GREEN}update${RESET} Review, commit & sync local changes. If the working tree is
|
||||
clean it simply runs 'git pull'.
|
||||
${GREEN}edit${RESET} Open the Panama repo in Neovim.
|
||||
${GREEN}doctor${RESET} Report what is actually running on this machine, rather
|
||||
than what was installed. Takes --summary for one line per check.
|
||||
${GREEN}test${RESET} Run every contract under tests/. Give it a pattern to run
|
||||
a subset: 'panama test dock' runs the ones matching 'dock'.
|
||||
${GREEN}upgrade${RESET} Re-run ./install from anywhere. Safe: every stage is
|
||||
idempotent and this is the documented upgrade path.
|
||||
${GREEN}help${RESET} Show this help (also -h, --help).
|
||||
|
||||
${BOLD}Options:${RESET}
|
||||
@@ -71,6 +80,9 @@ ${BOLD}Options:${RESET}
|
||||
${BOLD}Examples:${RESET}
|
||||
$PROGRAM update
|
||||
$PROGRAM edit
|
||||
$PROGRAM doctor --summary
|
||||
$PROGRAM test dock
|
||||
$PROGRAM upgrade
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -205,6 +217,100 @@ cmd_edit() {
|
||||
exec nvim .
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Command: doctor
|
||||
# ----------------------------------------------------------------------------
|
||||
#
|
||||
# The health check already exists and the installer already runs it; what it did
|
||||
# not have was a way to reach it from a terminal. Everything is passed straight
|
||||
# through, so --summary and anything added later work without this knowing about
|
||||
# them.
|
||||
cmd_doctor() {
|
||||
local doctor="$PANAMA_DIR/config/dot/quickshell/scripts/panama-doctor"
|
||||
if [[ ! -x "$doctor" ]]; then
|
||||
err "panama-doctor is missing from $doctor"
|
||||
exit 1
|
||||
fi
|
||||
exec "$doctor" "$@"
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Command: test
|
||||
# ----------------------------------------------------------------------------
|
||||
#
|
||||
# The contracts are the main safety net in this repository and had no entry
|
||||
# point: 121 executables with no runner and no mention in the README, which is
|
||||
# most of the way to not having them.
|
||||
#
|
||||
# Each runs in its own process and a failure does not stop the rest, because the
|
||||
# useful output is the whole list of what is broken rather than the first thing
|
||||
# that broke. The exit code is what a caller can act on.
|
||||
cmd_test() {
|
||||
local pattern="${1:-}"
|
||||
local -a suite=()
|
||||
|
||||
# Executables, plus the Python suites. Those are unittest files rather than
|
||||
# executables, and collecting only what has the executable bit would skip them
|
||||
# without saying so -- which is how all three came to be run by nothing at all.
|
||||
# A runner with a blind spot is worse than no runner, because it reports PASS.
|
||||
while IFS= read -r path; do
|
||||
[[ -x "$path" || "$path" == *_test.py ]] || continue
|
||||
[[ -z "$pattern" || "$path" == *"$pattern"* ]] && suite+=("$path")
|
||||
done < <(find "$PANAMA_DIR/tests" -type f -not -path '*/fixtures/*' -not -path '*__pycache__*' | sort)
|
||||
|
||||
if (( ${#suite[@]} == 0 )); then
|
||||
err "No contracts match '${pattern}'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
info "Running ${#suite[@]} contract(s)"
|
||||
local -a failed=()
|
||||
local path name
|
||||
local -a runner
|
||||
for path in "${suite[@]}"; do
|
||||
name="${path#"$PANAMA_DIR"/tests/}"
|
||||
if [[ "$path" == *_test.py ]]; then
|
||||
runner=(python3 "$path")
|
||||
else
|
||||
runner=("$path")
|
||||
fi
|
||||
if "${runner[@]}" >/dev/null 2>&1; then
|
||||
ok "$name"
|
||||
else
|
||||
err "$name"
|
||||
failed+=("$name")
|
||||
fi
|
||||
done
|
||||
|
||||
header "Result"
|
||||
if (( ${#failed[@]} == 0 )); then
|
||||
ok "${#suite[@]} contract(s) passed"
|
||||
return 0
|
||||
fi
|
||||
err "${#failed[@]} of ${#suite[@]} failed:"
|
||||
printf ' %s\n' "${failed[@]}" >&2
|
||||
warn "Run one on its own to see why: ${BOLD}${PANAMA_DIR}/tests/<name>${RESET}"
|
||||
return 1
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Command: upgrade
|
||||
# ----------------------------------------------------------------------------
|
||||
#
|
||||
# ./install is the upgrade path -- every stage is idempotent and re-running is
|
||||
# the documented way to repair a machine. This only saves remembering where the
|
||||
# repository lives.
|
||||
cmd_upgrade() {
|
||||
local installer="$PANAMA_DIR/install"
|
||||
if [[ ! -x "$installer" ]]; then
|
||||
err "The installer is missing from $installer"
|
||||
exit 1
|
||||
fi
|
||||
info "Re-running ${BOLD}${installer}${RESET}"
|
||||
cd "$PANAMA_DIR"
|
||||
exec "$installer" "$@"
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Dispatcher
|
||||
# ----------------------------------------------------------------------------
|
||||
@@ -213,6 +319,9 @@ main() {
|
||||
case "$cmd" in
|
||||
update) shift; cmd_update "$@" ;;
|
||||
edit) shift; cmd_edit "$@" ;;
|
||||
doctor) shift; cmd_doctor "$@" ;;
|
||||
test) shift; cmd_test "$@" ;;
|
||||
upgrade) shift; cmd_upgrade "$@" ;;
|
||||
help|-h|--help|"") usage ;;
|
||||
--version) printf '%s %s\n' "$PROGRAM" "$VERSION" ;;
|
||||
*)
|
||||
|
||||
@@ -24,8 +24,7 @@ else
|
||||
fi
|
||||
export PATH
|
||||
fi
|
||||
. "$HOME/.cargo/env"
|
||||
|
||||
# >>> Codex installer >>>
|
||||
export PATH="/home/gib/.local/bin:$PATH"
|
||||
# <<< Codex installer <<<
|
||||
# rustup writes this file, and initial-packages installs rustup rather than
|
||||
# running rustup-init -- so on a fresh machine it does not exist yet and an
|
||||
# unguarded source made every single shell start with an error.
|
||||
[ -f "$HOME/.cargo/env" ] && . "$HOME/.cargo/env"
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
{
|
||||
"overrides": [
|
||||
{
|
||||
"wmClass": "jetbrains-toolbox",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.amezin.ddterm",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.donadigo.eddy",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Conky",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Gnome-initial-setup",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "org.gnome.Calculator",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "gnome-terminal-preferences",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Guake",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "zoom",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "mpv",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Bitwarden",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Hidamari",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.mattjakeman.ExtensionManager",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Cider",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Ulauncher",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.nextcloud.desktopclient.nextcloud",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Emulator",
|
||||
"mode": "float"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
{
|
||||
"overrides": [
|
||||
{
|
||||
"wmClass": "jetbrains-toolbox",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.amezin.ddterm",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.donadigo.eddy",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Conky",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Gnome-initial-setup",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "org.gnome.Calculator",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "gnome-terminal-preferences",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Guake",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "zoom",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "mpv",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Bitwarden",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Hidamari",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.mattjakeman.ExtensionManager",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Cider",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Ulauncher",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.nextcloud.desktopclient.nextcloud",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Emulator",
|
||||
"mode": "float"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
{
|
||||
"overrides": [
|
||||
{
|
||||
"wmClass": "jetbrains-toolbox",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.amezin.ddterm",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.donadigo.eddy",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Conky",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Gnome-initial-setup",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "org.gnome.Calculator",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "gnome-terminal-preferences",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Guake",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "zoom",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "mpv",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Bitwarden",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Hidamari",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.mattjakeman.ExtensionManager",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Cider",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Ulauncher",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.nextcloud.desktopclient.nextcloud",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Emulator",
|
||||
"mode": "float"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
{
|
||||
"overrides": [
|
||||
{
|
||||
"wmClass": "jetbrains-toolbox",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.amezin.ddterm",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.donadigo.eddy",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Conky",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Gnome-initial-setup",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "org.gnome.Calculator",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "gnome-terminal-preferences",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Guake",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "zoom",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "mpv",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Bitwarden",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Hidamari",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.mattjakeman.ExtensionManager",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Cider",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Ulauncher",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.nextcloud.desktopclient.nextcloud",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Emulator",
|
||||
"mode": "float"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
{
|
||||
"overrides": [
|
||||
{
|
||||
"wmClass": "jetbrains-toolbox",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.amezin.ddterm",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.donadigo.eddy",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Conky",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Gnome-initial-setup",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "org.gnome.Calculator",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "gnome-terminal-preferences",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Guake",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "zoom",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "mpv",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Bitwarden",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Hidamari",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.mattjakeman.ExtensionManager",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Cider",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Ulauncher",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.nextcloud.desktopclient.nextcloud",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Emulator",
|
||||
"mode": "float"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
{
|
||||
"overrides": [
|
||||
{
|
||||
"wmClass": "jetbrains-toolbox",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.amezin.ddterm",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.donadigo.eddy",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Conky",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Gnome-initial-setup",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "org.gnome.Calculator",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "gnome-terminal-preferences",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Guake",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "zoom",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "mpv",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Bitwarden",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Hidamari",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.mattjakeman.ExtensionManager",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Cider",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Ulauncher",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.nextcloud.desktopclient.nextcloud",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Emulator",
|
||||
"mode": "float"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
{
|
||||
"overrides": [
|
||||
{
|
||||
"wmClass": "jetbrains-toolbox",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.amezin.ddterm",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Com.github.donadigo.eddy",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Conky",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Gnome-initial-setup",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "org.gnome.Calculator",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "gnome-terminal-preferences",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Guake",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "zoom",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Bitwarden",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Hidamari",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.mattjakeman.ExtensionManager",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Cider",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Ulauncher",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "com.nextcloud.desktopclient.nextcloud",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "mpv",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "Spotify",
|
||||
"mode": "float"
|
||||
},
|
||||
{
|
||||
"wmClass": "bitwarden",
|
||||
"mode": "float"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
.tiled {
|
||||
color: rgba(236, 94, 94, 1);
|
||||
opacity: 1;
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
.split {
|
||||
color: rgba(255, 246, 108, 1);
|
||||
opacity: 1;
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
.stacked {
|
||||
color: rgba(247, 162, 43, 1);
|
||||
opacity: 1;
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
.tabbed {
|
||||
color: rgba(17, 199, 224, 1);
|
||||
opacity: 1;
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
.floated {
|
||||
color: rgba(180, 167, 214, 1);
|
||||
border-width: 3px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.window-tiled-border {
|
||||
border-width: 3px;
|
||||
border-color: rgb(98,160,234);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.window-split-border {
|
||||
border-width: 3px;
|
||||
border-color: rgb(98,160,234);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
/* forge-36le: St does not paint an asymmetric (single-side) border when a
|
||||
* border-radius is set, so the directional hint never rendered. Drop the radius
|
||||
* on the one-edge variants; St paints a straight single-side border reliably. */
|
||||
|
||||
.window-split-horizontal {
|
||||
border-left-width: 0;
|
||||
border-top-width: 0;
|
||||
border-bottom-width: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.window-split-vertical {
|
||||
border-left-width: 0;
|
||||
border-top-width: 0;
|
||||
border-right-width: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.window-stacked-border {
|
||||
border-width: 3px;
|
||||
border-color: rgb(98,160,234);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.window-tabbed-border {
|
||||
border-width: 3px;
|
||||
border-color: rgb(98,160,234);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.window-tabbed-bg {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.window-tabbed-tab {
|
||||
background-color: rgba(54, 47, 45, 1);
|
||||
border-color: rgba(98,160,234,0.6);
|
||||
border-width: 1px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
margin: 1px;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.window-tabbed-tab-active {
|
||||
background-color: rgb(98,160,234);
|
||||
color: black;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.window-tabbed-tab-close {
|
||||
padding: 3px;
|
||||
margin: 4px;
|
||||
border-radius: 16px;
|
||||
width: 16px;
|
||||
background-color: #e06666;
|
||||
}
|
||||
|
||||
.window-tabbed-tab-icon {
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.window-floated-border {
|
||||
border-width: 3px;
|
||||
border-color: rgb(98,160,234);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.window-tilepreview-tiled {
|
||||
border-width: 1px;
|
||||
border-color: rgba(98,160,234,0.3);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
background-color: rgba(98,160,234,0.2);
|
||||
}
|
||||
|
||||
.window-tilepreview-stacked {
|
||||
border-width: 1px;
|
||||
border-color: rgba(98,160,234,0.3);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
background-color: rgba(98,160,234,0.2);
|
||||
}
|
||||
|
||||
.window-tilepreview-swap {
|
||||
border-width: 1px;
|
||||
border-color: rgba(162, 247, 43, 0.4);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
background-color: rgba(162, 247, 43, 0.4);
|
||||
}
|
||||
|
||||
.window-tilepreview-tabbed {
|
||||
border-width: 1px;
|
||||
border-color: rgba(98,160,234,0.3);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
background-color: rgba(98,160,234,0.2);
|
||||
}
|
||||
|
||||
/* Keybindings Cheatsheet Overlay */
|
||||
|
||||
.forge-cheatsheet {
|
||||
background-color: #2d2d2d;
|
||||
border-radius: 16px;
|
||||
padding: 24px 32px;
|
||||
border: 1px solid #4d4d4d;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.forge-cheatsheet-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.forge-cheatsheet-category {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
color: #78aeed;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.forge-cheatsheet-row {
|
||||
font-size: 12px;
|
||||
color: #eeeeec;
|
||||
margin: 4px 0;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.forge-cheatsheet-key {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
color: #f5c211;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.forge-cheatsheet-desc {
|
||||
color: #d0cfcc;
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
.tiled {
|
||||
color: rgba(236, 94, 94, 1);
|
||||
opacity: 1;
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
.split {
|
||||
color: rgba(255, 246, 108, 1);
|
||||
opacity: 1;
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
.stacked {
|
||||
color: rgba(247, 162, 43, 1);
|
||||
opacity: 1;
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
.tabbed {
|
||||
color: rgba(17, 199, 224, 1);
|
||||
opacity: 1;
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
.floated {
|
||||
color: rgba(180, 167, 214, 1);
|
||||
border-width: 3px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.window-tiled-border {
|
||||
border-width: 3px;
|
||||
border-color: rgb(98,160,234);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.window-split-border {
|
||||
border-width: 3px;
|
||||
border-color: rgb(98,160,234);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
/* forge-36le: St does not paint an asymmetric (single-side) border when a
|
||||
* border-radius is set, so the directional hint never rendered. Drop the radius
|
||||
* on the one-edge variants; St paints a straight single-side border reliably. */
|
||||
|
||||
.window-split-horizontal {
|
||||
border-left-width: 0;
|
||||
border-top-width: 0;
|
||||
border-bottom-width: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.window-split-vertical {
|
||||
border-left-width: 0;
|
||||
border-top-width: 0;
|
||||
border-right-width: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.window-stacked-border {
|
||||
border-width: 3px;
|
||||
border-color: rgb(98,160,234);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.window-tabbed-border {
|
||||
border-width: 3px;
|
||||
border-color: rgb(98,160,234);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.window-tabbed-bg {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.window-tabbed-tab {
|
||||
background-color: rgba(54, 47, 45, 1);
|
||||
border-color: rgba(98,160,234,0.6);
|
||||
border-width: 1px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
margin: 1px;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.window-tabbed-tab-active {
|
||||
background-color: rgb(98,160,234);
|
||||
color: black;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.window-tabbed-tab-close {
|
||||
padding: 3px;
|
||||
margin: 4px;
|
||||
border-radius: 16px;
|
||||
width: 16px;
|
||||
background-color: #e06666;
|
||||
}
|
||||
|
||||
.window-tabbed-tab-icon {
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.window-floated-border {
|
||||
border-width: 1px;
|
||||
border-color: rgb(98,160,234);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.window-tilepreview-tiled {
|
||||
border-width: 1px;
|
||||
border-color: rgba(98,160,234,0.3);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
background-color: rgba(98,160,234,0.2);
|
||||
}
|
||||
|
||||
.window-tilepreview-stacked {
|
||||
border-width: 1px;
|
||||
border-color: rgba(98,160,234,0.3);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
background-color: rgba(98,160,234,0.2);
|
||||
}
|
||||
|
||||
.window-tilepreview-swap {
|
||||
border-width: 1px;
|
||||
border-color: rgba(162, 247, 43, 0.4);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
background-color: rgba(162, 247, 43, 0.4);
|
||||
}
|
||||
|
||||
.window-tilepreview-tabbed {
|
||||
border-width: 1px;
|
||||
border-color: rgba(98,160,234,0.3);
|
||||
border-style: solid;
|
||||
border-radius: 14px;
|
||||
background-color: rgba(98,160,234,0.2);
|
||||
}
|
||||
|
||||
/* Keybindings Cheatsheet Overlay */
|
||||
|
||||
.forge-cheatsheet {
|
||||
background-color: #2d2d2d;
|
||||
border-radius: 16px;
|
||||
padding: 24px 32px;
|
||||
border: 1px solid #4d4d4d;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.forge-cheatsheet-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.forge-cheatsheet-category {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
color: #78aeed;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.forge-cheatsheet-row {
|
||||
font-size: 12px;
|
||||
color: #eeeeec;
|
||||
margin: 4px 0;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.forge-cheatsheet-key {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
color: #f5c211;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.forge-cheatsheet-desc {
|
||||
color: #d0cfcc;
|
||||
}
|
||||
@@ -88,7 +88,7 @@ entry.
|
||||
|
||||
`prefs.lua` never raises. A missing, empty, truncated, malformed, or
|
||||
wrong-typed settings file costs you your customizations and nothing else;
|
||||
`tests/hypr/prefs-fallback-contract.sh` pins that, including that Hyprland still
|
||||
`tests/hypr/prefs-fallback-contract` pins that, including that Hyprland still
|
||||
accepts the config in each of those states.
|
||||
|
||||
### Adding a keybind: use `bind`, not `hl.bind`
|
||||
@@ -120,7 +120,7 @@ Every `hl.bind` must pass a `description`. Hyprland reports Lua-defined binds
|
||||
with dispatcher `__lua` and a bytecode offset as the argument, so a bind without
|
||||
one has nothing readable beside its chord, and the Settings app drops it from the
|
||||
Input & Shortcuts page rather than showing a mystery row.
|
||||
`tests/quickshell/keybinds-contract.sh` fails if any bind lacks a description, so
|
||||
`tests/quickshell/keybinds-contract` fails if any bind lacks a description, so
|
||||
this cannot regress silently.
|
||||
|
||||
```lua
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
import qs.modules.settings
|
||||
import qs.services
|
||||
|
||||
ShellRoot {
|
||||
Item {
|
||||
width: 680
|
||||
height: editor.implicitHeight
|
||||
|
||||
AccentEditor {
|
||||
id: editor
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
ThemeProfilePicker {
|
||||
width: parent.width
|
||||
visible: false
|
||||
}
|
||||
|
||||
AccentPicker {
|
||||
width: parent.width
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "accent-controls-test"
|
||||
|
||||
function status(): string {
|
||||
return JSON.stringify(ThemeProfiles.activeProfile);
|
||||
}
|
||||
|
||||
function adjust(target: string, channel: string, ratio: real): string {
|
||||
editor.changeChannel(target, channel, ratio);
|
||||
return status();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -975,6 +975,19 @@ Singleton {
|
||||
{ value: "slate", label: "Slate" }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "themeProfileId", type: "string", def: "moon", group: "appearance",
|
||||
pattern: "^[a-z0-9][a-z0-9-]{0,63}$",
|
||||
label: "Selected theme profile",
|
||||
detail: "The shipped or saved theme currently applied to the desktop",
|
||||
internal: true
|
||||
},
|
||||
{
|
||||
key: "themeProfiles", type: "json", def: [], group: "appearance",
|
||||
label: "Saved theme profiles",
|
||||
detail: "Named custom colour schemes and accent pairs",
|
||||
internal: true
|
||||
},
|
||||
|
||||
// ── Application themes ─────────────────────────────────────────────
|
||||
// ColorScheme owns GTK's light/dark theme. These are the two theme
|
||||
|
||||
@@ -14,6 +14,7 @@ import Quickshell
|
||||
// QtQuick is required even though nothing visual is declared here: `color` is a
|
||||
// QtQuick value type, and Qt.rgba() lives in its JS namespace.
|
||||
import QtQuick
|
||||
import qs.services
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
@@ -60,30 +61,20 @@ Singleton {
|
||||
// Blue is the shipped Prism -- blue leading, orchid following -- and stays
|
||||
// the default.
|
||||
//
|
||||
// `gnome` is the nearest member of GNOME's own accent-color enum, which is
|
||||
// a fixed list of nine we do not get to extend. It is what libadwaita
|
||||
// The table itself lives in services/ThemeProfileModel.js so a curated
|
||||
// accent and a custom profile are the same kind of record. Its `gnome`
|
||||
// member is the nearest name in GNOME's own accent-color enum, which is a
|
||||
// fixed list of nine we do not get to extend; it is what libadwaita
|
||||
// applications -- Files, Papers, Loupe -- are told to use, so choosing an
|
||||
// accent here recolors them too instead of leaving them in GNOME blue.
|
||||
// Nearest by hue, not by name: "rose" maps to red rather than pink because
|
||||
// it is the red role in this palette.
|
||||
readonly property var accents: ({
|
||||
"blue": { dark: "#82aaff", darkSecondary: "#b172b0", light: "#2e7de9", lightSecondary: "#9854f1", label: "Prism blue", gnome: "blue" },
|
||||
"orchid": { dark: "#c099ff", darkSecondary: "#fca7ea", light: "#7847bd", lightSecondary: "#9854f1", label: "Orchid", gnome: "purple" },
|
||||
"teal": { dark: "#86e1fc", darkSecondary: "#82aaff", light: "#007197", lightSecondary: "#2e7de9", label: "Teal", gnome: "teal" },
|
||||
"green": { dark: "#c3e88d", darkSecondary: "#86e1fc", light: "#587539", lightSecondary: "#007197", label: "Green", gnome: "green" },
|
||||
"amber": { dark: "#ffc777", darkSecondary: "#ff966c", light: "#8c6c3e", lightSecondary: "#b15c00", label: "Amber", gnome: "yellow" },
|
||||
"orange": { dark: "#ff966c", darkSecondary: "#ff757f", light: "#b15c00", lightSecondary: "#c64343", label: "Orange", gnome: "orange" },
|
||||
"rose": { dark: "#ff757f", darkSecondary: "#c099ff", light: "#f52a65", lightSecondary: "#9854f1", label: "Rose", gnome: "red" },
|
||||
"slate": { dark: "#828bb8", darkSecondary: "#82aaff", light: "#6172b0", lightSecondary: "#2e7de9", label: "Slate", gnome: "slate" }
|
||||
})
|
||||
readonly property var accents: ThemeProfiles.curatedAccents
|
||||
readonly property var activeProfile: ThemeProfiles.activeProfile
|
||||
|
||||
// Falls back to blue for an unknown name, so a settings file written by a
|
||||
// newer Panama -- or edited by hand -- degrades to the shipped identity
|
||||
// rather than to an undefined color.
|
||||
readonly property var accentPair: root.accents[DesktopPreferences.get("accentName")] ?? root.accents["blue"]
|
||||
|
||||
readonly property color accent: root.dark ? root.accentPair.dark : root.accentPair.light
|
||||
readonly property color accentSecondary: root.dark ? root.accentPair.darkSecondary : root.accentPair.lightSecondary
|
||||
// ThemeProfiles validates every persisted record before it can become
|
||||
// active, so these bindings are both reactive and safe to expose as the
|
||||
// shell-wide colour roles.
|
||||
readonly property color accent: root.activeProfile.accent
|
||||
readonly property color accentSecondary: root.activeProfile.secondary
|
||||
readonly property color accentAlt: root.dark ? "#65bcff" : "#007197" // blue1, a lighter blue
|
||||
readonly property color cyan: root.dark ? "#86e1fc" : "#007197"
|
||||
readonly property color teal: root.dark ? "#4fd6be" : "#118c74"
|
||||
|
||||
@@ -6,6 +6,10 @@ import qs.config
|
||||
import qs.services
|
||||
|
||||
ShellRoot {
|
||||
// The isolated screen model begins with both fixture outputs so changing
|
||||
// it below exercises the same reactive topology path as a real hotplug.
|
||||
Component.onCompleted: Displays.screenOverride = ["DP-2", "HDMI-A-1"]
|
||||
|
||||
IpcHandler {
|
||||
target: "displays-test"
|
||||
|
||||
@@ -42,6 +46,7 @@ ShellRoot {
|
||||
function transactionStatus(): string {
|
||||
return JSON.stringify({
|
||||
layout: Displays.currentLayout(),
|
||||
primaryFirst: Displays.primaryFirstMonitors.map(monitor => monitor.name),
|
||||
pending: Displays.pendingRequestedLayout,
|
||||
previous: Displays.pendingPreviousLayout,
|
||||
reverting: Displays.revertExpectedLayout,
|
||||
@@ -141,5 +146,8 @@ ShellRoot {
|
||||
if (monitor) Displays.forget(monitor.name);
|
||||
}
|
||||
function refresh(): void { Displays.refresh(); }
|
||||
function setScreenModel(names: string): void {
|
||||
Displays.screenOverride = JSON.parse(names);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Exercises Migrations.applyWith against fixture steps and prints a verdict per
|
||||
// case. Run by tests/quickshell/migrations-contract.sh.
|
||||
// case. Run by tests/quickshell/migrations-contract.
|
||||
//
|
||||
// Fixture steps rather than the real list: the real one is empty until the
|
||||
// first breaking schema change, and a mechanism that has never been run against
|
||||
|
||||
@@ -177,12 +177,21 @@ PanelWindow {
|
||||
x: {
|
||||
if (!root.revealed)
|
||||
return root.position === "right" ? surface.width - root.revealStripHeight : 0;
|
||||
// A horizontal dock is centred on its edge, so the region has
|
||||
// to start where the body starts -- starting at 0 puts it over
|
||||
// the left of the screen while the pointer that summoned the
|
||||
// dock is in the middle, and the hover drops on the very frame
|
||||
// the dock arrives. A vertical dock reaches from the body out
|
||||
// to its own edge, which is x 0 on the left and the body on
|
||||
// the right.
|
||||
if (!root.vertical)
|
||||
return body.x;
|
||||
return root.position === "right" ? body.x : 0;
|
||||
}
|
||||
y: {
|
||||
if (!root.revealed)
|
||||
return root.vertical ? 0 : surface.height - root.revealStripHeight;
|
||||
return root.vertical ? body.y : body.y;
|
||||
return body.y;
|
||||
}
|
||||
width: {
|
||||
if (!root.revealed)
|
||||
@@ -201,6 +210,12 @@ PanelWindow {
|
||||
DockBody {
|
||||
id: body
|
||||
|
||||
onContextMenuRequested: (anchorItem, entry) => {
|
||||
dockContextMenu.anchorItem = anchorItem;
|
||||
dockContextMenu.entry = entry;
|
||||
dockContextMenu.visible = true;
|
||||
}
|
||||
|
||||
vertical: root.vertical
|
||||
leftSide: root.position === "left"
|
||||
|
||||
@@ -251,4 +266,8 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DockContextMenu {
|
||||
id: dockContextMenu
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +116,7 @@ Rectangle {
|
||||
|
||||
// The item the tooltip is currently describing, or null.
|
||||
property Item hoveredItem: null
|
||||
signal contextMenuRequested(Item anchorItem, var entry)
|
||||
|
||||
// Set by the Dock. A side dock runs the same strip down the screen instead
|
||||
// of across it.
|
||||
@@ -170,6 +171,7 @@ Rectangle {
|
||||
onEntered: root.hoveredItem = dockItem
|
||||
onExited: if (root.hoveredItem === dockItem)
|
||||
root.hoveredItem = null
|
||||
onContextMenuRequested: root.contextMenuRequested(dockItem, dockItem.entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// The dock's app menu. Desktop-entry actions stay first; the shell-owned
|
||||
// configuration route is deliberately last so it never displaces app actions.
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import qs.config
|
||||
import qs.modules.bar
|
||||
import qs.services
|
||||
import qs.widgets
|
||||
|
||||
PopupWindow {
|
||||
id: root
|
||||
|
||||
property Item anchorItem: null
|
||||
property var entry: null
|
||||
|
||||
anchor.item: root.anchorItem
|
||||
anchor.edges: Edges.Top | Edges.Left
|
||||
anchor.gravity: Edges.Top | Edges.Right
|
||||
anchor.margins.bottom: 8
|
||||
|
||||
implicitWidth: Math.max(menu.implicitWidth + Theme.popoverPadding * 2, 240)
|
||||
implicitHeight: menu.implicitHeight + Theme.popoverPadding * 2
|
||||
color: "transparent"
|
||||
visible: false
|
||||
grabFocus: true
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Theme.popoverRadius
|
||||
color: Theme.alpha(Theme.bgPopover, Theme.popoverAlpha)
|
||||
border.width: 1
|
||||
border.color: Theme.alpha(Theme.fg, 0.08)
|
||||
|
||||
PrismEdge {
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 1
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
inset: parent.radius
|
||||
}
|
||||
|
||||
Column {
|
||||
id: menu
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.popoverPadding
|
||||
spacing: 2
|
||||
|
||||
Repeater {
|
||||
id: applicationActions
|
||||
model: root.entry ? root.entry.actions : []
|
||||
|
||||
delegate: TrayMenuRow {
|
||||
required property var modelData
|
||||
|
||||
width: parent.width
|
||||
label: modelData.name
|
||||
onActivated: {
|
||||
modelData.execute();
|
||||
root.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
anchors.margins: 3
|
||||
visible: applicationActions.count > 0
|
||||
border.width: 0
|
||||
color: Theme.alpha(Theme.fg, 0.1)
|
||||
}
|
||||
|
||||
TrayMenuRow {
|
||||
width: parent.width
|
||||
label: "Dock settings"
|
||||
onActivated: {
|
||||
ShellState.openSettings("desktop");
|
||||
root.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ Item {
|
||||
// Emitted so DockBody can drive the single shared tooltip.
|
||||
signal entered
|
||||
signal exited
|
||||
signal contextMenuRequested
|
||||
|
||||
// The icon may grow past the cell on hover; the cell itself stays a fixed
|
||||
// size so the row doesn't reflow.
|
||||
@@ -115,7 +116,7 @@ Item {
|
||||
id: mouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton | Qt.MiddleButton
|
||||
acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
|
||||
|
||||
onEntered: root.entered()
|
||||
onExited: root.exited()
|
||||
@@ -126,6 +127,10 @@ Item {
|
||||
root.launch();
|
||||
return;
|
||||
}
|
||||
if (mev.button === Qt.RightButton) {
|
||||
root.contextMenuRequested();
|
||||
return;
|
||||
}
|
||||
if (root.running)
|
||||
root.focusNext();
|
||||
else
|
||||
|
||||
@@ -14,6 +14,7 @@ import Quickshell.Services.Notifications
|
||||
import qs.config
|
||||
import qs.services
|
||||
import qs.modules.quicksettings
|
||||
import qs.modules.bar
|
||||
import qs.widgets
|
||||
|
||||
Rectangle {
|
||||
@@ -127,11 +128,44 @@ Rectangle {
|
||||
onClicked: root.dismissed()
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: settingsMenuButton
|
||||
anchors.right: closeButton.left
|
||||
anchors.rightMargin: 2
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 6
|
||||
size: 24
|
||||
iconSize: 14
|
||||
tint: Theme.fgDim
|
||||
icon: "view-more-symbolic"
|
||||
iconFallback: "open-menu-symbolic"
|
||||
onClicked: settingsMenu.visible = !settingsMenu.visible
|
||||
}
|
||||
|
||||
Popover {
|
||||
id: settingsMenu
|
||||
anchorItem: settingsMenuButton
|
||||
|
||||
// A Column already measures itself from its children, and in Qt 6 both
|
||||
// implicit sizes are read-only on a positioner -- assigning them makes
|
||||
// the whole shell fail to load rather than just this menu.
|
||||
Column {
|
||||
TrayMenuRow {
|
||||
id: notificationSettings
|
||||
label: "Notification settings"
|
||||
onActivated: {
|
||||
ShellState.openSettings("notifications");
|
||||
settingsMenu.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: layout
|
||||
anchors.left: appIcon.visible ? appIcon.right : parent.left
|
||||
anchors.leftMargin: appIcon.visible ? 10 : 14
|
||||
anchors.right: image.visible ? image.left : closeButton.left
|
||||
anchors.right: image.visible ? image.left : settingsMenuButton.left
|
||||
anchors.rightMargin: 8
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 12
|
||||
|
||||
@@ -34,7 +34,14 @@ PanelWindow {
|
||||
implicitWidth: root.desiredWidth
|
||||
implicitHeight: 64
|
||||
color: "transparent"
|
||||
mask: Region {}
|
||||
mask: Region {
|
||||
item: inputMask
|
||||
}
|
||||
|
||||
Item {
|
||||
id: inputMask
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
WlrLayershell.namespace: "qs-popover-osd"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
@@ -145,6 +152,17 @@ PanelWindow {
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
onClicked: event => {
|
||||
if (event.button !== Qt.RightButton)
|
||||
return;
|
||||
ShellState.openSettings("accessibility");
|
||||
OsdState.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
|
||||
@@ -176,34 +176,34 @@ Item {
|
||||
height: aspect > 0 ? width / aspect : parent.height
|
||||
opacity: hasContent ? 1 : 0
|
||||
|
||||
// The capture context isn't ready the instant the layer surface is
|
||||
// told to become visible — it needs the surface to actually map,
|
||||
// which takes a frame or two. Calling captureFrame() before then
|
||||
// logs "no recording context is ready" and yields nothing, so
|
||||
// retry a few times and then give up quietly (the caption and app
|
||||
// icon are still shown, so a missing thumbnail is cosmetic).
|
||||
property int captureAttempts: 0
|
||||
// A ScreencopyView cannot tell us that its recording context is
|
||||
// ready before a capture. The enclosing overview gets that
|
||||
// context only after its first rendered frame, so defer the first
|
||||
// one-shot capture to that event. Retrying before then only emits
|
||||
// "no recording context is ready" warnings; if a capture later
|
||||
// cannot produce content, the app icon remains the fallback.
|
||||
property bool recordingReady: false
|
||||
|
||||
function tryCapture(): void {
|
||||
captureAttempts = 0;
|
||||
captureRetry.restart();
|
||||
}
|
||||
|
||||
// `shot`, not `parent`: Timer is a QtObject, so `parent` does not
|
||||
// resolve to the enclosing ScreencopyView.
|
||||
Timer {
|
||||
id: captureRetry
|
||||
interval: 80
|
||||
repeat: true
|
||||
running: false
|
||||
onTriggered: {
|
||||
if (shot.hasContent || shot.captureAttempts >= 6) {
|
||||
stop();
|
||||
if (shot.hasContent)
|
||||
return;
|
||||
if (!shot.recordingReady) {
|
||||
frameReady.restart();
|
||||
return;
|
||||
}
|
||||
shot.captureAttempts++;
|
||||
shot.captureFrame();
|
||||
}
|
||||
|
||||
FrameAnimation {
|
||||
id: frameReady
|
||||
running: false
|
||||
onTriggered: {
|
||||
running = false;
|
||||
if (shot.hasContent)
|
||||
return;
|
||||
shot.recordingReady = true;
|
||||
shot.tryCapture();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: tryCapture()
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
// Advanced accent editing remains a labelled, keyboard-operable extension of
|
||||
// the named fast path. Hue is always accompanied by saturation, value, a
|
||||
// numeric readout, and the two-colour preview supplied by Appearance.
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.config
|
||||
import qs.services
|
||||
import qs.widgets
|
||||
import "../../services/ThemeProfileModel.js" as ThemeProfileModel
|
||||
|
||||
Column {
|
||||
id: editor
|
||||
|
||||
width: parent ? parent.width : 620
|
||||
spacing: 0
|
||||
|
||||
property string pickerTarget: "primary"
|
||||
property string lastError: ""
|
||||
|
||||
readonly property var primaryHsv: ThemeProfileModel.hexToHsv(
|
||||
ThemeProfiles.activeProfile.accent) || ({ h: 0, s: 0, v: 0 })
|
||||
readonly property var secondaryHsv: ThemeProfileModel.hexToHsv(
|
||||
ThemeProfiles.activeProfile.secondary) || ({ h: 0, s: 0, v: 0 })
|
||||
|
||||
function changeChannel(target: string, channel: string, ratio: real): void {
|
||||
const source = target === "primary" ? editor.primaryHsv : editor.secondaryHsv;
|
||||
const next = { h: source.h, s: source.s, v: source.v };
|
||||
next[channel] = Math.round(Math.max(0, Math.min(1, ratio))
|
||||
* (channel === "h" ? 360 : 100));
|
||||
const changed = ThemeProfileModel.hsvToHex(next.h, next.s, next.v);
|
||||
const primary = target === "primary" ? changed : ThemeProfiles.activeProfile.accent;
|
||||
const secondary = target === "secondary" ? changed : ThemeProfiles.activeProfile.secondary;
|
||||
ThemeProfiles.setAccentPair(primary, secondary);
|
||||
}
|
||||
|
||||
function pick(target: string): void {
|
||||
if (screenPicker.running)
|
||||
return;
|
||||
editor.pickerTarget = target;
|
||||
editor.lastError = "";
|
||||
screenPicker.exec(["hyprpicker", "--format=hex", "--lowercase-hex", "--quiet", "--no-fancy"]);
|
||||
}
|
||||
|
||||
function acceptPicked(value: string): void {
|
||||
const picked = String(value).trim().toLowerCase();
|
||||
if (ThemeProfileModel.hexToHsv(picked) === null) {
|
||||
editor.lastError = "The sampled colour was not valid.";
|
||||
return;
|
||||
}
|
||||
const primary = editor.pickerTarget === "primary"
|
||||
? picked : ThemeProfiles.activeProfile.accent;
|
||||
const secondary = editor.pickerTarget === "secondary"
|
||||
? picked : ThemeProfiles.activeProfile.secondary;
|
||||
ThemeProfiles.setAccentPair(primary, secondary);
|
||||
}
|
||||
|
||||
component HsvRow: SettingRow {
|
||||
id: root
|
||||
|
||||
required property string target
|
||||
required property string channel
|
||||
required property real channelValue
|
||||
required property real channelMaximum
|
||||
property string suffix: "%"
|
||||
|
||||
controlWidth: 280
|
||||
|
||||
Item {
|
||||
id: keyboardSlider
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 270
|
||||
height: 32
|
||||
activeFocusOnTab: true
|
||||
|
||||
Accessible.role: Accessible.Slider
|
||||
Accessible.name: root.label
|
||||
Accessible.description: Math.round(root.channelValue) + root.suffix
|
||||
+ ", range 0 to " + root.channelMaximum
|
||||
Accessible.focusable: true
|
||||
Accessible.focused: activeFocus
|
||||
Accessible.onIncreaseAction: keyboardSlider.step(1)
|
||||
Accessible.onDecreaseAction: keyboardSlider.step(-1)
|
||||
|
||||
function step(direction: int): void {
|
||||
const increment = root.channel === "h" ? 1 : 1;
|
||||
const value = Math.max(0, Math.min(root.channelMaximum,
|
||||
root.channelValue + direction * increment));
|
||||
editor.changeChannel(root.target, root.channel, value / root.channelMaximum);
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Left || event.key === Qt.Key_Down) {
|
||||
keyboardSlider.step(-1);
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Right || event.key === Qt.Key_Up) {
|
||||
keyboardSlider.step(1);
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Home) {
|
||||
editor.changeChannel(root.target, root.channel, 0);
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_End) {
|
||||
editor.changeChannel(root.target, root.channel, 1);
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: readout.left
|
||||
anchors.rightMargin: 10
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 24
|
||||
radius: 9
|
||||
color: "transparent"
|
||||
border.width: keyboardSlider.activeFocus ? 2 : 1
|
||||
border.color: keyboardSlider.activeFocus ? Theme.accentSecondary : Theme.alpha(Theme.fg, 0.08)
|
||||
|
||||
ValueSlider {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 4
|
||||
value: root.channelMaximum > 0 ? root.channelValue / root.channelMaximum : 0
|
||||
onMoved: ratio => editor.changeChannel(root.target, root.channel, ratio)
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: readout
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 56
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: Math.round(root.channelValue) + root.suffix
|
||||
color: Theme.fgDim
|
||||
font.family: Theme.fontFamily
|
||||
font.features: Theme.tabularFigures
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HsvRow {
|
||||
target: "primary"; channel: "h"; channelValue: editor.primaryHsv.h; channelMaximum: 360
|
||||
suffix: "°"; label: "Primary hue"; detail: "Colour family, measured from 0 to 360 degrees"
|
||||
}
|
||||
HsvRow {
|
||||
target: "primary"; channel: "s"; channelValue: editor.primaryHsv.s; channelMaximum: 100
|
||||
label: "Primary saturation"; detail: "Colour intensity from grey to vivid"
|
||||
}
|
||||
HsvRow {
|
||||
target: "primary"; channel: "v"; channelValue: editor.primaryHsv.v; channelMaximum: 100
|
||||
label: "Primary value"; detail: "Brightness from black to full colour"
|
||||
}
|
||||
HsvRow {
|
||||
target: "secondary"; channel: "h"; channelValue: editor.secondaryHsv.h; channelMaximum: 360
|
||||
suffix: "°"; label: "Secondary hue"; detail: "Colour family at the far end of the Prism gradient"
|
||||
}
|
||||
HsvRow {
|
||||
target: "secondary"; channel: "s"; channelValue: editor.secondaryHsv.s; channelMaximum: 100
|
||||
label: "Secondary saturation"; detail: "Colour intensity from grey to vivid"
|
||||
}
|
||||
HsvRow {
|
||||
target: "secondary"; channel: "v"; channelValue: editor.secondaryHsv.v; channelMaximum: 100
|
||||
label: "Secondary value"; detail: "Brightness from black to full colour"
|
||||
}
|
||||
|
||||
SettingRow {
|
||||
label: "Pick colour from screen"
|
||||
detail: editor.lastError !== ""
|
||||
? editor.lastError
|
||||
: "Sample either end of the accent gradient with hyprpicker"
|
||||
divider: false
|
||||
controlWidth: 330
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 7
|
||||
|
||||
SettingsButton {
|
||||
text: "Pick primary from screen"
|
||||
enabled: !screenPicker.running
|
||||
activeFocusOnTab: enabled
|
||||
border.width: activeFocus ? 2 : 1
|
||||
border.color: activeFocus ? Theme.accent : Theme.alpha(Theme.fg, 0.08)
|
||||
onClicked: editor.pick("primary")
|
||||
Keys.onReturnPressed: if (enabled) editor.pick("primary")
|
||||
Keys.onSpacePressed: if (enabled) editor.pick("primary")
|
||||
}
|
||||
|
||||
SettingsButton {
|
||||
text: "Pick secondary from screen"
|
||||
enabled: !screenPicker.running
|
||||
activeFocusOnTab: enabled
|
||||
border.width: activeFocus ? 2 : 1
|
||||
border.color: activeFocus ? Theme.accent : Theme.alpha(Theme.fg, 0.08)
|
||||
onClicked: editor.pick("secondary")
|
||||
Keys.onReturnPressed: if (enabled) editor.pick("secondary")
|
||||
Keys.onSpacePressed: if (enabled) editor.pick("secondary")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: screenPicker
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: editor.acceptPicked(this.text)
|
||||
}
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (exitCode !== 0)
|
||||
editor.lastError = "Screen colour picking was cancelled or unavailable.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ Flow {
|
||||
|
||||
spacing: 10
|
||||
|
||||
readonly property string current: DesktopPreferences.get("accentName") || "blue"
|
||||
readonly property string current: ThemeProfiles.activeAccentName
|
||||
|
||||
// The schema's own option list, not Theme.accents directly -- two lists
|
||||
// hand-kept in sync is how they drift. This is the same pattern
|
||||
@@ -60,6 +60,8 @@ Flow {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: swatch
|
||||
|
||||
width: 46
|
||||
height: 46
|
||||
radius: 23
|
||||
@@ -67,8 +69,25 @@ Flow {
|
||||
color: "transparent"
|
||||
// The ring sits outside the gradient rather than over it, so a
|
||||
// selected swatch still shows its true colors.
|
||||
border.width: entry.selected ? 2 : 1
|
||||
border.color: entry.selected ? Theme.fg : Theme.alpha(Theme.fg, 0.14)
|
||||
border.width: swatch.activeFocus || entry.selected ? 2 : 1
|
||||
border.color: swatch.activeFocus
|
||||
? Theme.accentSecondary
|
||||
: (entry.selected ? Theme.fg : Theme.alpha(Theme.fg, 0.14))
|
||||
activeFocusOnTab: true
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: entry.pair.label + " accent"
|
||||
Accessible.description: String(entry.start) + " to " + String(entry.end)
|
||||
Accessible.focusable: true
|
||||
Accessible.focused: activeFocus
|
||||
|
||||
function choose(): void {
|
||||
if (!ThemeProfiles.useCuratedAccent(entry.modelData))
|
||||
console.warn("AccentPicker: accent was rejected", entry.modelData);
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: swatch.choose()
|
||||
Keys.onSpacePressed: swatch.choose()
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
@@ -81,6 +100,9 @@ Flow {
|
||||
GradientStop { position: 1.0; color: entry.end }
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler { cursorShape: Qt.PointingHandCursor }
|
||||
TapHandler { onTapped: swatch.choose() }
|
||||
}
|
||||
|
||||
// Always shown, not a tooltip. Telling swatches apart by color is
|
||||
|
||||
@@ -129,18 +129,26 @@ SettingsPage {
|
||||
|
||||
SettingsCard {
|
||||
visible: root.tab === "theme"
|
||||
title: "Color scheme"
|
||||
// Not "Color scheme" any more: the card holds the saved themes as well
|
||||
// as the scheme, and a theme carries both ends of the accent with it.
|
||||
title: "Theme"
|
||||
subtitle: ColorScheme.lastError !== ""
|
||||
? ColorScheme.lastError
|
||||
: "Light is Tokyo Night Day, the official light variant — the same hues at a different lightness, so the blue-into-orchid signature survives the switch. Applications and window borders follow."
|
||||
: "Start with Moon, Moon Rose, or Day; saved themes capture the scheme and both ends of the Prism accent."
|
||||
|
||||
ChoiceRow { setting: "colorScheme" }
|
||||
ThemeProfilePicker {
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
// Drawn as the gradient each accent produces rather than a flat dot,
|
||||
// because the gradient is what is being chosen.
|
||||
AccentPicker {
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
AccentEditor {
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
|
||||
@@ -22,7 +22,7 @@ SettingsPage {
|
||||
|
||||
property string selectedOutput: ""
|
||||
readonly property var monitor: Displays.monitorNamed(root.selectedOutput)
|
||||
?? (Displays.monitors.length > 0 ? Displays.monitors[0] : null)
|
||||
?? (Displays.primaryFirstMonitors.length > 0 ? Displays.primaryFirstMonitors[0] : null)
|
||||
readonly property string currentMode: root.monitor
|
||||
? root.monitor.mode
|
||||
: ""
|
||||
@@ -38,7 +38,8 @@ SettingsPage {
|
||||
|
||||
function syncSelectedOutput(): void {
|
||||
if (!Displays.monitorNamed(root.selectedOutput))
|
||||
root.selectedOutput = Displays.monitors.length > 0 ? Displays.monitors[0].name : "";
|
||||
root.selectedOutput = Displays.primaryFirstMonitors.length > 0
|
||||
? Displays.primaryFirstMonitors[0].name : "";
|
||||
}
|
||||
|
||||
// Probing I2C for DDC-capable monitors takes on the order of a second, so
|
||||
@@ -137,7 +138,7 @@ SettingsPage {
|
||||
ChoiceGrid {
|
||||
width: parent.width
|
||||
label: "Display"
|
||||
options: Displays.monitors.map(monitor => ({
|
||||
options: Displays.primaryFirstMonitors.map(monitor => ({
|
||||
value: monitor.name,
|
||||
label: monitor.description || monitor.name
|
||||
}))
|
||||
|
||||
@@ -91,7 +91,7 @@ layout.
|
||||
|
||||
Mirrors must remain the same schema-backed control, never a second preference
|
||||
or a copied default. Additions to this table require a concrete discoverability
|
||||
reason and an update to `tests/quickshell/settings-ownership-contract.sh`.
|
||||
reason and an update to `tests/quickshell/settings-ownership-contract`.
|
||||
|
||||
Window border color follows the same ownership rule. The inactive border is a
|
||||
**scheme-relative role** owned by `ColorScheme.qml`: it changes only to retain
|
||||
@@ -127,7 +127,7 @@ store. A row never needs to know which kind it holds.
|
||||
the value in a different JSON field per type — `int`, `bool`, `float`, `str`,
|
||||
and `css` for gaps (a four-value box). Declaring the wrong one does not fail
|
||||
loudly: it makes every write to that key look *rejected*, and the user sees an
|
||||
error for a change that worked. `tests/quickshell/schema-hypr-shape-contract.sh`
|
||||
error for a change that worked. `tests/quickshell/schema-hypr-shape-contract`
|
||||
asks the compositor for the real shape of every mapped option.
|
||||
|
||||
**Never trust an exit code from `hyprctl`.** `keyword` refuses to work on a
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
// Named themes are the first layer of Appearance: shipped profiles stay
|
||||
// immutable, while saved profiles can be selected or removed in place.
|
||||
|
||||
import QtQuick
|
||||
import qs.config
|
||||
import qs.services
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
width: parent ? parent.width : 620
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: ThemeProfiles.profiles
|
||||
|
||||
SettingRow {
|
||||
id: profileRow
|
||||
|
||||
required property var modelData
|
||||
|
||||
label: profileRow.modelData.name
|
||||
detail: (profileRow.modelData.scheme === "light" ? "Light" : "Dark")
|
||||
+ " · " + profileRow.modelData.accent + " → " + profileRow.modelData.secondary
|
||||
controlWidth: profileRow.modelData.shipped ? 88 : 170
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 6
|
||||
|
||||
SettingsButton {
|
||||
id: useButton
|
||||
text: profileRow.modelData.id === ThemeProfiles.activeProfile.id ? "Selected" : "Use"
|
||||
enabled: profileRow.modelData.id !== ThemeProfiles.activeProfile.id
|
||||
activeFocusOnTab: enabled
|
||||
border.width: activeFocus ? 2 : (tone === "accent" ? 0 : 1)
|
||||
border.color: activeFocus ? Theme.accent : Theme.alpha(Theme.fg, 0.08)
|
||||
onClicked: ThemeProfiles.selectProfile(profileRow.modelData.id)
|
||||
Keys.onReturnPressed: if (enabled) ThemeProfiles.selectProfile(profileRow.modelData.id)
|
||||
Keys.onSpacePressed: if (enabled) ThemeProfiles.selectProfile(profileRow.modelData.id)
|
||||
}
|
||||
|
||||
SettingsButton {
|
||||
id: deleteButton
|
||||
visible: !profileRow.modelData.shipped
|
||||
text: "Delete"
|
||||
activeFocusOnTab: visible
|
||||
border.width: activeFocus ? 2 : 1
|
||||
border.color: activeFocus ? Theme.danger : Theme.alpha(Theme.fg, 0.08)
|
||||
onClicked: ThemeProfiles.deleteProfile(profileRow.modelData.id)
|
||||
Keys.onReturnPressed: ThemeProfiles.deleteProfile(profileRow.modelData.id)
|
||||
Keys.onSpacePressed: ThemeProfiles.deleteProfile(profileRow.modelData.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingRow {
|
||||
label: "Save current theme"
|
||||
detail: "Stores this scheme and accent pair under a unique name"
|
||||
divider: false
|
||||
controlWidth: 292
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 7
|
||||
|
||||
Rectangle {
|
||||
width: 196
|
||||
height: 31
|
||||
radius: 8
|
||||
color: Theme.alpha(Theme.fg, 0.05)
|
||||
border.width: nameInput.activeFocus ? 2 : 1
|
||||
border.color: nameInput.activeFocus ? Theme.accent : Theme.alpha(Theme.fg, 0.12)
|
||||
|
||||
TextInput {
|
||||
id: nameInput
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
activeFocusOnTab: true
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
maximumLength: 40
|
||||
color: Theme.fg
|
||||
selectionColor: Theme.alpha(Theme.accent, 0.35)
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
Accessible.name: "Theme profile name"
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: nameInput.text === "" && !nameInput.activeFocus
|
||||
text: "Theme name"
|
||||
color: Theme.fgMuted
|
||||
font: nameInput.font
|
||||
}
|
||||
|
||||
onAccepted: saveButton.save()
|
||||
}
|
||||
}
|
||||
|
||||
SettingsButton {
|
||||
id: saveButton
|
||||
text: "Save"
|
||||
tone: "accent"
|
||||
activeFocusOnTab: true
|
||||
border.width: activeFocus ? 2 : 0
|
||||
border.color: activeFocus ? Theme.fg : "transparent"
|
||||
|
||||
function save(): void {
|
||||
if (ThemeProfiles.saveProfile(nameInput.text))
|
||||
nameInput.text = "";
|
||||
}
|
||||
|
||||
onClicked: save()
|
||||
Keys.onReturnPressed: save()
|
||||
Keys.onSpacePressed: save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,13 @@ Column {
|
||||
width: parent ? parent.width : 620
|
||||
spacing: 4
|
||||
|
||||
onOutputsChanged: root.syncSelectedOutput()
|
||||
|
||||
function syncSelectedOutput(): void {
|
||||
if (!root.outputs.includes(root.selectedOutput))
|
||||
root.selectedOutput = root.outputs.length > 0 ? root.outputs[0] : "";
|
||||
}
|
||||
|
||||
ChoiceGrid {
|
||||
width: parent.width
|
||||
label: "Wallpaper mode"
|
||||
|
||||
@@ -80,3 +80,5 @@ SearchPicker 1.0 SearchPicker.qml
|
||||
OnlineAccountsPage 1.0 OnlineAccountsPage.qml
|
||||
AccentPicker 1.0 AccentPicker.qml
|
||||
TextFieldRow 1.0 TextFieldRow.qml
|
||||
AccentEditor 1.0 AccentEditor.qml
|
||||
ThemeProfilePicker 1.0 ThemeProfilePicker.qml
|
||||
|
||||
@@ -17,8 +17,10 @@ status_file="$state_dir/hyprlock-status.json"
|
||||
fallback="$config_home/hypr/hyprlock.conf"
|
||||
temporary="$generated.tmp.$$"
|
||||
status_temporary="$status_file.tmp.$$"
|
||||
shipped_wallpaper="$HOME/Pictures/Wallpapers/faroe_islands.jpg"
|
||||
|
||||
settings_valid=false
|
||||
wallpaper_warning=""
|
||||
|
||||
cleanup() {
|
||||
rm -f "$temporary" "$status_temporary" 2>/dev/null || true
|
||||
@@ -76,7 +78,23 @@ read_object() {
|
||||
}
|
||||
|
||||
valid_path() {
|
||||
[[ "$1" == /* && "$1" != *","* && "$1" != *$'\n'* ]]
|
||||
[[ "$1" == /* && "$1" != *","* && "$1" != *$'\n'* && -f "$1" && -r "$1" ]]
|
||||
}
|
||||
|
||||
resolve_wallpaper_path() {
|
||||
local candidate="$1"
|
||||
if valid_path "$candidate"; then
|
||||
resolved_wallpaper="$candidate"
|
||||
return
|
||||
fi
|
||||
if [[ -n "$candidate" ]]; then
|
||||
wallpaper_warning="One or more lock-screen wallpapers were unavailable; a safe fallback is in use."
|
||||
fi
|
||||
if valid_path "$shipped_wallpaper"; then
|
||||
resolved_wallpaper="$shipped_wallpaper"
|
||||
else
|
||||
resolved_wallpaper="screenshot"
|
||||
fi
|
||||
}
|
||||
|
||||
# Same 8 named accents as config/Theme.qml's `accents` map (kept in sync by
|
||||
@@ -134,10 +152,9 @@ load_preferences() {
|
||||
wallpaper_mode="$(read_string wallpaperMode single)"
|
||||
[[ "$wallpaper_mode" == single || "$wallpaper_mode" == slideshow || "$wallpaper_mode" == per-monitor ]] \
|
||||
|| wallpaper_mode=single
|
||||
wallpaper_path="$(read_string wallpaperPath '')"
|
||||
if ! valid_path "$wallpaper_path"; then
|
||||
wallpaper_path="$HOME/Pictures/Wallpapers/faroe_islands.jpg"
|
||||
fi
|
||||
wallpaper_warning=""
|
||||
resolve_wallpaper_path "$(read_string wallpaperPath '')"
|
||||
wallpaper_path="$resolved_wallpaper"
|
||||
wallpaper_assignments="$(read_object wallpaperPerMonitor)"
|
||||
|
||||
case "$blur_level" in
|
||||
@@ -218,8 +235,9 @@ emit_backgrounds() {
|
||||
candidate="$(jq -r --arg output "$output" \
|
||||
'if has($output) and (.[$output] | type) == "string" then .[$output] else "" end' \
|
||||
<<<"$wallpaper_assignments" 2>/dev/null || true)"
|
||||
if valid_path "$candidate"; then
|
||||
assigned="$candidate"
|
||||
if [[ -n "$candidate" ]]; then
|
||||
resolve_wallpaper_path "$candidate"
|
||||
assigned="$resolved_wallpaper"
|
||||
fi
|
||||
fi
|
||||
emit_background "$output" "$assigned"
|
||||
@@ -344,7 +362,7 @@ generate() {
|
||||
write_status false "$fallback" true "The lock-screen configuration could not be generated."
|
||||
return 1
|
||||
fi
|
||||
write_status true "$generated" false ""
|
||||
write_status true "$generated" false "$wallpaper_warning"
|
||||
}
|
||||
|
||||
status() {
|
||||
|
||||
@@ -150,12 +150,12 @@ def build() -> dict[Path, str]:
|
||||
files: dict[Path, str] = {}
|
||||
for page, label in pages():
|
||||
words = keywords_for(page, routing, schema, extras)
|
||||
files[OUTPUT_DIR / f"settings-{page}.sh"] = command_for(page, label, words)
|
||||
files[OUTPUT_DIR / f"settings-{page}"] = command_for(page, label, words)
|
||||
return files
|
||||
|
||||
|
||||
def existing() -> set[Path]:
|
||||
return {path for path in OUTPUT_DIR.glob("settings-*.sh")}
|
||||
return {path for path in OUTPUT_DIR.glob("settings-*")}
|
||||
|
||||
|
||||
def main(arguments: list[str]) -> int:
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Finds wallpaper candidates from fixed roots passed as separate arguments.
|
||||
# Keeping paths as argv values avoids shell interpolation for names containing
|
||||
# quotes, spaces, or other shell syntax.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
find "$@" -maxdepth 2 -type f \
|
||||
\( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.webp' \) \
|
||||
-printf '%T@ %p\n' 2>/dev/null \
|
||||
| sort -rn \
|
||||
| cut -d' ' -f2- \
|
||||
| awk 'NR <= 60'
|
||||
@@ -93,6 +93,14 @@ Singleton {
|
||||
function onRevisionChanged(): void { coalesce.restart(); }
|
||||
}
|
||||
|
||||
// Profile selection and custom edits both update preferences, but binding
|
||||
// directly to the effective profile also documents the in-shell ownership
|
||||
// boundary and keeps this propagation reactive if that storage changes.
|
||||
Connections {
|
||||
target: ThemeProfiles
|
||||
function onActiveProfileChanged(): void { coalesce.restart(); }
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: coalesce
|
||||
interval: 250
|
||||
|
||||
@@ -29,8 +29,24 @@ Singleton {
|
||||
// [{ name, description, width, height, refreshRate, scale, transform,
|
||||
// modes: [{ label, mode, width, height, refresh }] }]
|
||||
property var monitors: []
|
||||
// Quickshell.screens is the topology authority. The override is only the
|
||||
// isolated harness model; normal sessions always observe Quickshell.
|
||||
property var screenOverride: null
|
||||
property string lastError: ""
|
||||
|
||||
readonly property var screenModel: Array.isArray(root.screenOverride)
|
||||
? root.screenOverride : Quickshell.screens
|
||||
readonly property string screenSignature: root.screenModel
|
||||
.map(screen => typeof screen === "string" ? screen : screen.name)
|
||||
.filter(name => !!name)
|
||||
.sort()
|
||||
.join("|")
|
||||
readonly property var primaryFirstMonitors: root.monitors.slice().sort((left, right) => {
|
||||
if (left.primary !== right.primary)
|
||||
return left.primary ? -1 : 1;
|
||||
return left.name.localeCompare(right.name);
|
||||
})
|
||||
|
||||
// Set while a change is applied but not yet confirmed.
|
||||
property var pendingPreviousLayout: null
|
||||
property var pendingRequestedLayout: null
|
||||
@@ -113,6 +129,17 @@ Singleton {
|
||||
|
||||
Component.onCompleted: root.refresh()
|
||||
|
||||
onScreenSignatureChanged: root.reconcileTopology()
|
||||
|
||||
// A hotplug can invalidate the unconfirmed layout while the confirmation
|
||||
// is visible. Read the current compositor layout first; the normal queued
|
||||
// rollback then filters out any output that has disappeared.
|
||||
function reconcileTopology(): void {
|
||||
root.refresh();
|
||||
if (root.awaitingConfirmation)
|
||||
root.revertWithMessage("A display was connected or disconnected, so Panama restored the previous setting.");
|
||||
}
|
||||
|
||||
function refresh(): bool {
|
||||
if (!query.running) {
|
||||
query.generation = root.operationGeneration;
|
||||
|
||||
@@ -226,7 +226,7 @@ Singleton {
|
||||
// A bind with no description cannot be presented usefully --
|
||||
// the dispatcher is "__lua" and the argument is a bytecode
|
||||
// offset. Showing the chord alone would be worse than omitting
|
||||
// it, and tests/quickshell/keybinds-contract.sh fails the build
|
||||
// it, and tests/quickshell/keybinds-contract fails the build
|
||||
// if any exist, so this should never be reached in practice.
|
||||
if (description === "")
|
||||
continue;
|
||||
|
||||
@@ -136,7 +136,10 @@ Singleton {
|
||||
{ label: "Per-display wallpaper", detail: "Assign a different image to each connected display", page: "appearance" },
|
||||
{ label: "Arrange displays", detail: "Drag connected displays into their physical positions", page: "displays" },
|
||||
{ label: "Monitor position", detail: "Set where each display sits in the desktop", page: "displays" },
|
||||
{ label: "Primary display", detail: "Choose the display that anchors the desktop", page: "displays" }
|
||||
{ label: "Primary display", detail: "Choose the display that anchors the desktop", page: "displays" },
|
||||
{ label: "Theme profiles", detail: "Switch between Moon, Moon Rose, Day, and saved themes", page: "appearance" },
|
||||
{ label: "Advanced accent", detail: "Adjust primary and secondary hue, saturation, and value", page: "appearance" },
|
||||
{ label: "Pick colour from screen", detail: "Sample an accent colour with hyprpicker", page: "appearance" }
|
||||
]
|
||||
|
||||
function pageFor(group: string): string {
|
||||
|
||||
@@ -0,0 +1,419 @@
|
||||
var MAX_NAME_LENGTH = 40;
|
||||
|
||||
var SHIPPED = [
|
||||
{
|
||||
id: "moon",
|
||||
name: "Moon",
|
||||
scheme: "dark",
|
||||
accent: "#82aaff",
|
||||
secondary: "#b172b0",
|
||||
shipped: true
|
||||
},
|
||||
{
|
||||
id: "moon-rose",
|
||||
name: "Moon Rose",
|
||||
scheme: "dark",
|
||||
accent: "#ff757f",
|
||||
secondary: "#c099ff",
|
||||
shipped: true
|
||||
},
|
||||
{
|
||||
id: "day",
|
||||
name: "Day",
|
||||
scheme: "light",
|
||||
accent: "#2e7de9",
|
||||
secondary: "#9854f1",
|
||||
shipped: true
|
||||
}
|
||||
];
|
||||
|
||||
// The curated accents. `gnome` is the nearest member of GNOME's own
|
||||
// accent-color enum, which is a fixed list of nine we do not get to extend. It
|
||||
// is what libadwaita applications -- Files, Papers, Loupe -- are told to use, so
|
||||
// choosing an accent here recolors them too instead of leaving them in GNOME
|
||||
// blue. Nearest by hue, not by name: "rose" maps to red rather than pink
|
||||
// because it is the red role in this palette. adwaita-accent-contract reads
|
||||
// this table and fails when a member is missing or is not in GNOME's enum.
|
||||
var CURATED = {
|
||||
blue: {
|
||||
dark: "#82aaff", darkSecondary: "#b172b0",
|
||||
light: "#2e7de9", lightSecondary: "#9854f1",
|
||||
label: "Prism blue",
|
||||
gnome: "blue"
|
||||
},
|
||||
orchid: {
|
||||
dark: "#c099ff", darkSecondary: "#fca7ea",
|
||||
light: "#7847bd", lightSecondary: "#9854f1",
|
||||
label: "Orchid",
|
||||
gnome: "purple"
|
||||
},
|
||||
teal: {
|
||||
dark: "#86e1fc", darkSecondary: "#82aaff",
|
||||
light: "#007197", lightSecondary: "#2e7de9",
|
||||
label: "Teal",
|
||||
gnome: "teal"
|
||||
},
|
||||
green: {
|
||||
dark: "#c3e88d", darkSecondary: "#86e1fc",
|
||||
light: "#587539", lightSecondary: "#007197",
|
||||
label: "Green",
|
||||
gnome: "green"
|
||||
},
|
||||
amber: {
|
||||
dark: "#ffc777", darkSecondary: "#ff966c",
|
||||
light: "#8c6c3e", lightSecondary: "#b15c00",
|
||||
label: "Amber",
|
||||
gnome: "yellow"
|
||||
},
|
||||
orange: {
|
||||
dark: "#ff966c", darkSecondary: "#ff757f",
|
||||
light: "#b15c00", lightSecondary: "#c64343",
|
||||
label: "Orange",
|
||||
gnome: "orange"
|
||||
},
|
||||
rose: {
|
||||
dark: "#ff757f", darkSecondary: "#c099ff",
|
||||
light: "#f52a65", lightSecondary: "#9854f1",
|
||||
label: "Rose",
|
||||
gnome: "red"
|
||||
},
|
||||
slate: {
|
||||
dark: "#828bb8", darkSecondary: "#82aaff",
|
||||
light: "#6172b0", lightSecondary: "#2e7de9",
|
||||
label: "Slate",
|
||||
gnome: "slate"
|
||||
}
|
||||
};
|
||||
|
||||
function copyProfile(profile) {
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.name,
|
||||
scheme: profile.scheme,
|
||||
accent: profile.accent,
|
||||
secondary: profile.secondary,
|
||||
shipped: profile.shipped === true
|
||||
};
|
||||
}
|
||||
|
||||
function shippedProfiles() {
|
||||
return SHIPPED.map(copyProfile);
|
||||
}
|
||||
|
||||
function curatedAccents() {
|
||||
var result = {};
|
||||
Object.keys(CURATED).forEach(function(name) {
|
||||
result[name] = Object.assign({}, CURATED[name]);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function isScheme(value) {
|
||||
return value === "dark" || value === "light";
|
||||
}
|
||||
|
||||
function normalizedColor(value) {
|
||||
var color = String(value || "").trim().toLowerCase();
|
||||
return /^#[0-9a-f]{6}$/.test(color) ? color : null;
|
||||
}
|
||||
|
||||
function normalizeStoredProfile(value) {
|
||||
if (!value || typeof value !== "object" || value.shipped === true)
|
||||
return null;
|
||||
|
||||
var id = String(value.id || "").trim();
|
||||
var name = String(value.name || "").trim();
|
||||
var accent = normalizedColor(value.accent);
|
||||
var secondary = normalizedColor(value.secondary);
|
||||
if (!/^custom-[a-z0-9][a-z0-9-]{0,56}$/.test(id)
|
||||
|| name.length === 0 || name.length > MAX_NAME_LENGTH
|
||||
|| !isScheme(value.scheme) || !accent || !secondary)
|
||||
return null;
|
||||
|
||||
return {
|
||||
id: id,
|
||||
name: name,
|
||||
scheme: value.scheme,
|
||||
accent: accent,
|
||||
secondary: secondary,
|
||||
shipped: false
|
||||
};
|
||||
}
|
||||
|
||||
function validCustomProfiles(values) {
|
||||
if (!Array.isArray(values))
|
||||
return [];
|
||||
|
||||
var ids = {};
|
||||
var names = {};
|
||||
SHIPPED.forEach(function(profile) {
|
||||
ids[profile.id] = true;
|
||||
names[profile.name.toLowerCase()] = true;
|
||||
});
|
||||
|
||||
var result = [];
|
||||
values.forEach(function(value) {
|
||||
var profile = normalizeStoredProfile(value);
|
||||
if (!profile)
|
||||
return;
|
||||
var foldedName = profile.name.toLowerCase();
|
||||
if (ids[profile.id] || names[foldedName])
|
||||
return;
|
||||
ids[profile.id] = true;
|
||||
names[foldedName] = true;
|
||||
result.push(profile);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function profileCatalog(values) {
|
||||
return shippedProfiles().concat(validCustomProfiles(values));
|
||||
}
|
||||
|
||||
function boundedName(value) {
|
||||
var name = String(value || "").trim();
|
||||
if (!name)
|
||||
name = "Custom theme";
|
||||
return name.slice(0, MAX_NAME_LENGTH);
|
||||
}
|
||||
|
||||
function uniqueName(value, profiles) {
|
||||
var requested = boundedName(value);
|
||||
var names = {};
|
||||
profiles.forEach(function(profile) {
|
||||
names[profile.name.toLowerCase()] = true;
|
||||
});
|
||||
if (!names[requested.toLowerCase()])
|
||||
return requested;
|
||||
|
||||
for (var suffix = 2; suffix < 10000; suffix++) {
|
||||
var ending = " " + suffix;
|
||||
var candidate = requested.slice(0, MAX_NAME_LENGTH - ending.length) + ending;
|
||||
if (!names[candidate.toLowerCase()])
|
||||
return candidate;
|
||||
}
|
||||
return requested.slice(0, MAX_NAME_LENGTH - 6) + " 10000";
|
||||
}
|
||||
|
||||
function slug(value) {
|
||||
var result = String(value || "").toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, "-")
|
||||
.replace(/^-+|-+$/g, "")
|
||||
.slice(0, 48)
|
||||
.replace(/-+$/g, "");
|
||||
return result || "theme";
|
||||
}
|
||||
|
||||
function uniqueId(name, profiles) {
|
||||
var base = "custom-" + slug(name);
|
||||
var ids = {};
|
||||
profiles.forEach(function(profile) { ids[profile.id] = true; });
|
||||
if (!ids[base])
|
||||
return base;
|
||||
for (var suffix = 2; suffix < 10000; suffix++) {
|
||||
var candidate = base.slice(0, 57 - String(suffix).length) + "-" + suffix;
|
||||
if (!ids[candidate])
|
||||
return candidate;
|
||||
}
|
||||
return base.slice(0, 52) + "-10000";
|
||||
}
|
||||
|
||||
function createCustomProfile(values, input) {
|
||||
var customs = validCustomProfiles(values);
|
||||
var catalog = shippedProfiles().concat(customs);
|
||||
var scheme = input && input.scheme;
|
||||
var accent = normalizedColor(input && input.accent);
|
||||
var secondary = normalizedColor(input && input.secondary);
|
||||
if (!isScheme(scheme) || !accent || !secondary)
|
||||
return { profiles: customs, profile: null };
|
||||
|
||||
var name = uniqueName(input && input.name, catalog);
|
||||
var profile = {
|
||||
id: uniqueId(name, catalog),
|
||||
name: name,
|
||||
scheme: scheme,
|
||||
accent: accent,
|
||||
secondary: secondary,
|
||||
shipped: false
|
||||
};
|
||||
return { profiles: customs.concat([profile]), profile: profile };
|
||||
}
|
||||
|
||||
function findProfile(values, id) {
|
||||
var catalog = profileCatalog(values);
|
||||
for (var index = 0; index < catalog.length; index++) {
|
||||
if (catalog[index].id === id)
|
||||
return catalog[index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function editProfile(values, selected, changes) {
|
||||
var customs = validCustomProfiles(values);
|
||||
if (!selected || typeof selected !== "object")
|
||||
return { profiles: customs, profile: null };
|
||||
|
||||
var accent = normalizedColor(changes && changes.accent !== undefined
|
||||
? changes.accent : selected.accent);
|
||||
var secondary = normalizedColor(changes && changes.secondary !== undefined
|
||||
? changes.secondary : selected.secondary);
|
||||
var scheme = changes && changes.scheme !== undefined ? changes.scheme : selected.scheme;
|
||||
if (!isScheme(scheme) || !accent || !secondary)
|
||||
return { profiles: customs, profile: null };
|
||||
|
||||
if (selected.shipped === true) {
|
||||
return createCustomProfile(customs, {
|
||||
name: selected.name + " custom",
|
||||
scheme: scheme,
|
||||
accent: accent,
|
||||
secondary: secondary
|
||||
});
|
||||
}
|
||||
|
||||
var stored = normalizeStoredProfile(selected);
|
||||
if (!stored)
|
||||
return { profiles: customs, profile: null };
|
||||
|
||||
var updated = Object.assign({}, stored, {
|
||||
scheme: scheme,
|
||||
accent: accent,
|
||||
secondary: secondary
|
||||
});
|
||||
var found = false;
|
||||
var next = customs.map(function(profile) {
|
||||
if (profile.id !== updated.id)
|
||||
return profile;
|
||||
found = true;
|
||||
return updated;
|
||||
});
|
||||
if (!found)
|
||||
next.push(updated);
|
||||
return { profiles: next, profile: updated };
|
||||
}
|
||||
|
||||
function deleteProfile(values, id) {
|
||||
var customs = validCustomProfiles(values);
|
||||
for (var shippedIndex = 0; shippedIndex < SHIPPED.length; shippedIndex++) {
|
||||
if (SHIPPED[shippedIndex].id === id)
|
||||
return { profiles: customs, removed: false };
|
||||
}
|
||||
var next = customs.filter(function(profile) { return profile.id !== id; });
|
||||
return { profiles: next, removed: next.length !== customs.length };
|
||||
}
|
||||
|
||||
function curatedPair(name, scheme) {
|
||||
var entry = CURATED[name] || CURATED.blue;
|
||||
if (scheme === "light")
|
||||
return { accent: entry.light, secondary: entry.lightSecondary };
|
||||
return { accent: entry.dark, secondary: entry.darkSecondary };
|
||||
}
|
||||
|
||||
function matchingShippedProfile(scheme, accent, secondary) {
|
||||
var first = normalizedColor(accent);
|
||||
var second = normalizedColor(secondary);
|
||||
for (var index = 0; index < SHIPPED.length; index++) {
|
||||
var profile = SHIPPED[index];
|
||||
if (profile.scheme === scheme && profile.accent === first && profile.secondary === second)
|
||||
return copyProfile(profile);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function curatedNameForProfile(profile) {
|
||||
if (!profile || !isScheme(profile.scheme))
|
||||
return "";
|
||||
var accent = normalizedColor(profile.accent);
|
||||
var secondary = normalizedColor(profile.secondary);
|
||||
var names = Object.keys(CURATED);
|
||||
for (var index = 0; index < names.length; index++) {
|
||||
var name = names[index];
|
||||
var pair = curatedPair(name, profile.scheme);
|
||||
if (pair.accent === accent && pair.secondary === secondary)
|
||||
return name;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function clamp(value, minimum, maximum) {
|
||||
return Math.max(minimum, Math.min(maximum, Number(value)));
|
||||
}
|
||||
|
||||
function channelHex(value) {
|
||||
var text = Math.round(value).toString(16);
|
||||
return text.length < 2 ? "0" + text : text;
|
||||
}
|
||||
|
||||
function hsvToHex(hue, saturation, value) {
|
||||
var h = Number(hue);
|
||||
var s = Number(saturation);
|
||||
var v = Number(value);
|
||||
if (!isFinite(h) || !isFinite(s) || !isFinite(v))
|
||||
return null;
|
||||
h = ((h % 360) + 360) % 360;
|
||||
s = clamp(s, 0, 100) / 100;
|
||||
v = clamp(v, 0, 100) / 100;
|
||||
|
||||
var chroma = v * s;
|
||||
var section = h / 60;
|
||||
var x = chroma * (1 - Math.abs(section % 2 - 1));
|
||||
var red = 0;
|
||||
var green = 0;
|
||||
var blue = 0;
|
||||
if (section < 1) { red = chroma; green = x; }
|
||||
else if (section < 2) { red = x; green = chroma; }
|
||||
else if (section < 3) { green = chroma; blue = x; }
|
||||
else if (section < 4) { green = x; blue = chroma; }
|
||||
else if (section < 5) { red = x; blue = chroma; }
|
||||
else { red = chroma; blue = x; }
|
||||
var match = v - chroma;
|
||||
return "#" + channelHex((red + match) * 255)
|
||||
+ channelHex((green + match) * 255)
|
||||
+ channelHex((blue + match) * 255);
|
||||
}
|
||||
|
||||
function hexToHsv(value) {
|
||||
var color = normalizedColor(value);
|
||||
if (!color)
|
||||
return null;
|
||||
var red = parseInt(color.slice(1, 3), 16) / 255;
|
||||
var green = parseInt(color.slice(3, 5), 16) / 255;
|
||||
var blue = parseInt(color.slice(5, 7), 16) / 255;
|
||||
var maximum = Math.max(red, green, blue);
|
||||
var minimum = Math.min(red, green, blue);
|
||||
var delta = maximum - minimum;
|
||||
var hue = 0;
|
||||
if (delta !== 0) {
|
||||
if (maximum === red)
|
||||
hue = 60 * (((green - blue) / delta) % 6);
|
||||
else if (maximum === green)
|
||||
hue = 60 * ((blue - red) / delta + 2);
|
||||
else
|
||||
hue = 60 * ((red - green) / delta + 4);
|
||||
}
|
||||
if (hue < 0)
|
||||
hue += 360;
|
||||
return {
|
||||
h: Math.round(hue),
|
||||
s: Math.round((maximum === 0 ? 0 : delta / maximum) * 100),
|
||||
v: Math.round(maximum * 100)
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = {
|
||||
MAX_NAME_LENGTH: MAX_NAME_LENGTH,
|
||||
shippedProfiles: shippedProfiles,
|
||||
curatedAccents: curatedAccents,
|
||||
validCustomProfiles: validCustomProfiles,
|
||||
profileCatalog: profileCatalog,
|
||||
createCustomProfile: createCustomProfile,
|
||||
findProfile: findProfile,
|
||||
editProfile: editProfile,
|
||||
deleteProfile: deleteProfile,
|
||||
curatedPair: curatedPair,
|
||||
matchingShippedProfile: matchingShippedProfile,
|
||||
curatedNameForProfile: curatedNameForProfile,
|
||||
hsvToHex: hsvToHex,
|
||||
hexToHsv: hexToHsv
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import qs.config
|
||||
import "ThemeProfileModel.js" as ThemeProfileModel
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property var customProfiles: ThemeProfileModel.validCustomProfiles(
|
||||
DesktopPreferences.get("themeProfiles"))
|
||||
readonly property var curatedAccents: ThemeProfileModel.curatedAccents()
|
||||
readonly property var profiles: ThemeProfileModel.profileCatalog(root.customProfiles)
|
||||
readonly property string activeId: DesktopPreferences.get("themeProfileId") || "moon"
|
||||
readonly property var activeProfile: ThemeProfileModel.findProfile(
|
||||
root.customProfiles, root.activeId) || ThemeProfileModel.shippedProfiles()[0]
|
||||
readonly property string activeAccentName: ThemeProfileModel.curatedNameForProfile(
|
||||
root.activeProfile)
|
||||
|
||||
property bool reconciling: false
|
||||
|
||||
function commitActive(profile: var): bool {
|
||||
if (!profile)
|
||||
return false;
|
||||
|
||||
// Scheme first: if another control changed it, the reconciliation hook
|
||||
// may briefly select Day/Moon before this final stable profile id lands.
|
||||
const schemeAccepted = SystemSettings.commitPreference("colorScheme", profile.scheme);
|
||||
const profileAccepted = SystemSettings.commitPreference("themeProfileId", profile.id);
|
||||
return schemeAccepted && profileAccepted;
|
||||
}
|
||||
|
||||
function selectProfile(id: string): bool {
|
||||
return root.commitActive(ThemeProfileModel.findProfile(root.customProfiles, id));
|
||||
}
|
||||
|
||||
// Editing a shipped profile creates a saved custom copy. Editing a custom
|
||||
// profile updates only that record. ThemeProfileModel enforces both rules,
|
||||
// leaving this singleton responsible only for validated preference routing.
|
||||
function setAccentPair(accent: string, secondary: string): bool {
|
||||
const result = ThemeProfileModel.editProfile(root.customProfiles, root.activeProfile, {
|
||||
accent: accent,
|
||||
secondary: secondary
|
||||
});
|
||||
if (!result.profile)
|
||||
return false;
|
||||
if (!SystemSettings.commitPreference("themeProfiles", result.profiles))
|
||||
return false;
|
||||
return SystemSettings.commitPreference("themeProfileId", result.profile.id);
|
||||
}
|
||||
|
||||
function saveProfile(name: string): bool {
|
||||
const result = ThemeProfileModel.createCustomProfile(root.customProfiles, {
|
||||
name: name,
|
||||
scheme: root.activeProfile.scheme,
|
||||
accent: root.activeProfile.accent,
|
||||
secondary: root.activeProfile.secondary
|
||||
});
|
||||
if (!result.profile)
|
||||
return false;
|
||||
if (!SystemSettings.commitPreference("themeProfiles", result.profiles))
|
||||
return false;
|
||||
return SystemSettings.commitPreference("themeProfileId", result.profile.id);
|
||||
}
|
||||
|
||||
function deleteProfile(id: string): bool {
|
||||
const result = ThemeProfileModel.deleteProfile(root.customProfiles, id);
|
||||
if (!result.removed)
|
||||
return false;
|
||||
if (!SystemSettings.commitPreference("themeProfiles", result.profiles))
|
||||
return false;
|
||||
if (root.activeId === id)
|
||||
return root.selectProfile(DesktopPreferences.get("colorScheme") === "light" ? "day" : "moon");
|
||||
return true;
|
||||
}
|
||||
|
||||
function useCuratedAccent(name: string): bool {
|
||||
const scheme = DesktopPreferences.get("colorScheme") === "light" ? "light" : "dark";
|
||||
const pair = ThemeProfileModel.curatedPair(name, scheme);
|
||||
const shipped = ThemeProfileModel.matchingShippedProfile(
|
||||
scheme, pair.accent, pair.secondary);
|
||||
const accepted = shipped
|
||||
? root.commitActive(shipped)
|
||||
: root.setAccentPair(pair.accent, pair.secondary);
|
||||
if (accepted)
|
||||
SystemSettings.commitPreference("accentName", name);
|
||||
return accepted;
|
||||
}
|
||||
|
||||
function reconcileScheme(): void {
|
||||
if (root.reconciling)
|
||||
return;
|
||||
const scheme = DesktopPreferences.get("colorScheme") === "light" ? "light" : "dark";
|
||||
if (root.activeProfile.scheme === scheme)
|
||||
return;
|
||||
root.reconciling = true;
|
||||
SystemSettings.commitPreference("themeProfileId", scheme === "light" ? "day" : "moon");
|
||||
root.reconciling = false;
|
||||
}
|
||||
|
||||
Component.onCompleted: root.reconcileScheme()
|
||||
|
||||
Connections {
|
||||
target: DesktopPreferences
|
||||
function onRevisionChanged(): void { root.reconcileScheme(); }
|
||||
}
|
||||
}
|
||||
@@ -45,9 +45,10 @@ Singleton {
|
||||
readonly property string outputSignature: root.outputNames().slice().sort().join("|")
|
||||
|
||||
property var outputNames: function() {
|
||||
if (Array.isArray(root.outputOverride))
|
||||
return root.outputOverride.slice();
|
||||
return Quickshell.screens.map(screen => screen.name).filter(name => !!name);
|
||||
const outputs = Array.isArray(root.outputOverride)
|
||||
? root.outputOverride.slice()
|
||||
: Quickshell.screens.map(screen => screen.name).filter(name => !!name);
|
||||
return root.primaryFirstOutputs(outputs);
|
||||
}
|
||||
|
||||
readonly property var searchRoots: [
|
||||
@@ -59,10 +60,8 @@ Singleton {
|
||||
|
||||
Process {
|
||||
id: scan
|
||||
command: ["bash", "-lc",
|
||||
"find " + root.searchRoots.map(dir => `'${dir}'`).join(" ")
|
||||
+ " -maxdepth 2 -type f \\( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.webp' \\)"
|
||||
+ " -printf '%T@ %p\\n' 2>/dev/null | sort -rn | cut -d' ' -f2- | head -60"]
|
||||
command: [Quickshell.shellDir + "/scripts/panama-wallpaper-scan"]
|
||||
.concat(root.searchRoots)
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.available = this.text.split("\n")
|
||||
@@ -164,6 +163,18 @@ Singleton {
|
||||
activeQuery.running = true;
|
||||
}
|
||||
|
||||
// The saved primary role anchors Panama's per-monitor choices. Keep the
|
||||
// compositor's remaining order stable so a reconnect does not reshuffle
|
||||
// the rest of the picker unnecessarily.
|
||||
function primaryFirstOutputs(outputs: var): var {
|
||||
const stored = DesktopPreferences.get("displays");
|
||||
const layouts = stored && typeof stored === "object" ? stored : {};
|
||||
const primary = outputs.find(output => layouts[output]?.primary === true);
|
||||
return primary === undefined
|
||||
? outputs
|
||||
: [primary].concat(outputs.filter(output => output !== primary));
|
||||
}
|
||||
|
||||
function candidates(extra: var): var {
|
||||
const result = [];
|
||||
const discovered = Array.isArray(root.candidateOverride)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
import qs.config
|
||||
import qs.services
|
||||
|
||||
ShellRoot {
|
||||
IpcHandler {
|
||||
target: "theme-profiles-test"
|
||||
|
||||
function status(): string {
|
||||
return JSON.stringify({
|
||||
active: ThemeProfiles.activeProfile,
|
||||
profiles: ThemeProfiles.profiles,
|
||||
stored: DesktopPreferences.get("themeProfiles")
|
||||
});
|
||||
}
|
||||
|
||||
function select(id: string): bool {
|
||||
return ThemeProfiles.selectProfile(id);
|
||||
}
|
||||
|
||||
function scheme(value: string): bool {
|
||||
return SystemSettings.commitPreference("colorScheme", value);
|
||||
}
|
||||
|
||||
function edit(accent: string, secondary: string): bool {
|
||||
return ThemeProfiles.setAccentPair(accent, secondary);
|
||||
}
|
||||
|
||||
function save(name: string): bool {
|
||||
return ThemeProfiles.saveProfile(name);
|
||||
}
|
||||
|
||||
function remove(id: string): bool {
|
||||
return ThemeProfiles.deleteProfile(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import qs.services
|
||||
|
||||
ShellRoot {
|
||||
Component.onCompleted: {
|
||||
Wallpaper.outputOverride = ["DP-2", "HDMI-A-1"];
|
||||
Wallpaper.outputOverride = ["HDMI-A-1", "DP-2"];
|
||||
Wallpaper.candidateOverride = ["/images/a.jpg", "/images/b.jpg", "/images/c.jpg"];
|
||||
Wallpaper.startupRestoreEnabled = false;
|
||||
Wallpaper.slideshowIntervalOverrideMs = 60000;
|
||||
@@ -63,7 +63,9 @@ ShellRoot {
|
||||
assignments: DesktopPreferences.get("wallpaperPerMonitor"),
|
||||
slideshowPath: Wallpaper.slideshowPath,
|
||||
shuffleBag: Wallpaper.shuffleBag,
|
||||
slideshowTimerRunning: Wallpaper.slideshowTimerRunning
|
||||
slideshowTimerRunning: Wallpaper.slideshowTimerRunning,
|
||||
scanning: Wallpaper.scanning,
|
||||
available: Wallpaper.available
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,16 @@ ShellRoot {
|
||||
return JSON.stringify(root.calls);
|
||||
}
|
||||
|
||||
function selectOutput(output: string): void {
|
||||
controls.selectedOutput = output;
|
||||
}
|
||||
|
||||
function updateOutputs(outputs: string): void {
|
||||
controls.outputs = outputs === "" ? [] : outputs.split("|");
|
||||
}
|
||||
|
||||
function selectedOutput(): string { return controls.selectedOutput; }
|
||||
|
||||
function states(): string {
|
||||
picker.mode = "single";
|
||||
const single = {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# Firefox chrome
|
||||
|
||||
This directory is **not Panama's work**. It is [Edge-Frfox][upstream] — "a
|
||||
Firefox userChrome.css theme that aims to recreate the look and feel of
|
||||
Microsoft Edge" — vendored here, plus two local tweaks.
|
||||
|
||||
- Upstream: <https://github.com/bmFtZQ/edge-frfox>
|
||||
- License: MIT
|
||||
|
||||
`link-dotfiles` places `user.js` and `chrome/` into the Firefox profile. The
|
||||
preferences in `user.js` are what the theme needs to work at all —
|
||||
`toolkit.legacyUserProfileCustomizations.stylesheets` in particular, without
|
||||
which Firefox ignores `chrome/` entirely.
|
||||
|
||||
## Why it is vendored rather than fetched
|
||||
|
||||
The same reason the cursor theme is: downloading it during the install would add
|
||||
a URL that can rot, and this repository is trying to stop installing things that
|
||||
way. 732K in git once, and then the theme is simply there.
|
||||
|
||||
## Two things to know before editing
|
||||
|
||||
`userChrome.css` imports `custom.css` last, deliberately — local changes go
|
||||
there, not into the upstream files, so a future update to the theme is a
|
||||
directory replacement rather than a merge. The imports of
|
||||
`tweaks/hide-tabs-bar.css` and `tweaks/shrink-sideberry.css` are personal
|
||||
choices carried over from sunhat, not upstream defaults.
|
||||
|
||||
`userChrome.css` is unsupported by Mozilla. It breaks on major Firefox releases,
|
||||
and when it does, the fix is upstream's or none. Panama installs Firefox as a
|
||||
second engine to test pages against; if the theme ever costs more than it is
|
||||
worth, deleting this directory and the `link-dotfiles` block that reads it
|
||||
leaves a working browser behind.
|
||||
|
||||
[upstream]: https://github.com/bmFtZQ/edge-frfox
|
||||
@@ -0,0 +1,105 @@
|
||||
/* Edge-Frfox - content/common.css */
|
||||
|
||||
/*
|
||||
MARK: Default colours
|
||||
*/
|
||||
@-moz-document url-prefix(about:) {
|
||||
:root {
|
||||
--in-content-page-color: light-dark(#252525, #fff) !important;
|
||||
--in-content-page-background: light-dark(#f7f7f7, #252525) !important;
|
||||
--in-content-deemphasized-text: light-dark(#666, #bbb) !important;
|
||||
--in-content-box-background: light-dark(#fff, #333) !important;
|
||||
--in-content-box-background-odd: light-dark(rgb(0 0 0 / .05), rgb(255 255 255 / .05)) !important;
|
||||
--in-content-box-info-background: light-dark(#f7f7f7, rgb(255 255 255 / 0.15)) !important;
|
||||
--in-content-icon-color: light-dark(#666, #fff) !important;
|
||||
--in-content-accent-color: light-dark(#006CBE, #75B6E8) !important;
|
||||
--in-content-accent-color-active: light-dark(#1683D8, #4C98D1) !important;
|
||||
--in-content-border-hover: light-dark(rgb(0 0 0 / .5), rgb(255 255 255 / .3)) !important;
|
||||
--in-content-border-invalid: var(--red-50) !important;
|
||||
--in-content-border-color: light-dark(#bebebe, rgb(255 255 255 / .2)) !important;
|
||||
--in-content-error-text-color: light-dark(#FF848A, #FF9AA2) !important;
|
||||
--in-content-link-color: light-dark(#0078D4, #75B6E8) !important;
|
||||
--in-content-link-color-hover: light-dark(#006CBE, #63ADE5) !important;
|
||||
--in-content-link-color-active: light-dark(#0749AC, #4C98D1) !important;
|
||||
--in-content-link-color-visited: light-dark(#0078D4, #75B6E8) !important;
|
||||
--link-color: var(--in-content-link-color) !important;
|
||||
--link-color-hover: var(--in-content-link-color-hover) !important;
|
||||
--link-color-active: var(--in-content-link-color-active) !important;
|
||||
--link-color-visited: var(--in-content-link-color-visited) !important;
|
||||
--in-content-button-text-color: var(--in-content-text-color) !important;
|
||||
--in-content-button-text-color-hover: var(--in-content-text-color) !important;
|
||||
--in-content-button-text-color-active: var(--in-content-button-text-color-hover) !important;
|
||||
--in-content-button-background: light-dark(#EDEDED, #3B3B3B) !important;
|
||||
--in-content-button-background-hover: light-dark(#e5e5e5, #545454) !important;
|
||||
--in-content-button-background-active: light-dark(#d5d5d5, #606060) !important;
|
||||
--in-content-primary-button-text-color: light-dark(#fff, #fff) !important;
|
||||
--in-content-primary-button-background: light-dark(#0061e0, #006CBE) !important;
|
||||
--in-content-primary-button-background-hover: light-dark(#0250bb, #0078D4) !important;
|
||||
--in-content-primary-button-background-active: light-dark(#053e94, #005CA3) !important;
|
||||
--color-accent-primary: light-dark(#0078D4, #006CBE) !important;
|
||||
--color-accent-primary-hover: light-dark(#006CBE, #0078D4) !important;
|
||||
--color-accent-primary-active: light-dark(#0749AC, #005CA3) !important;
|
||||
--in-content-danger-button-background: light-dark(#DF4951, #E12424) !important;
|
||||
--in-content-danger-button-background-hover: light-dark(#EA656C, #F94343) !important;
|
||||
--in-content-danger-button-background-active: light-dark(#BB353C, #AA1E1E) !important;
|
||||
--in-content-focus-outline-color: light-dark(rgb(0 85 215 / .5), #63ADE5) !important;
|
||||
--focus-outline-color: var(--in-content-focus-outline-color) !important;
|
||||
--in-content-table-background: light-dark(#f7f7f7, #252525) !important;
|
||||
--in-content-text-color: var(--in-content-page-color) !important;
|
||||
--border-interactive-color: light-dark(#929292, #858585) !important;
|
||||
scrollbar-color:
|
||||
light-dark(rgb(37 37 37 / .4), rgb(255 255 255 / .4))
|
||||
light-dark(#f7f7f7, #252525) !important;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: light-dark(#0078D4, #93B8E7);
|
||||
color: light-dark(#fff, #000);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Preferences page
|
||||
*/
|
||||
@-moz-document url-prefix(about:preferences),
|
||||
url-prefix(about:addons) {
|
||||
.category[selected] {
|
||||
position: relative !important;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
.category[selected]:not(:hover, :active) {
|
||||
background-color: var(--in-content-button-background) !important;
|
||||
}
|
||||
|
||||
.category[selected]::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset-block: 0;
|
||||
margin-block: auto;
|
||||
height: 24px;
|
||||
width: 3px;
|
||||
left: 2px;
|
||||
background-color: var(--in-content-accent-color);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
button.tab-button:hover {
|
||||
border-block-color: transparent var(--in-content-box-border-color) !important;
|
||||
}
|
||||
|
||||
button.tab-button[selected],
|
||||
button.tab-button[selected]:hover {
|
||||
border-block-color: transparent currentColor !important;
|
||||
}
|
||||
|
||||
.toggle-button {
|
||||
--toggle-dot-margin: 4px !important;
|
||||
--toggle-width: 40px !important;
|
||||
--toggle-height: 20px !important;
|
||||
--toggle-background-color: transparent !important;
|
||||
--toggle-dot-background-color: var(--in-content-page-color) !important;
|
||||
--toggle-dot-background-color-on-pressed: #fff !important;
|
||||
--toggle-dot-transform-x: calc(var(--toggle-width) - 2 * var(--toggle-dot-margin) - 2 * var(--toggle-border-width) - var(--toggle-dot-width)) !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
/* Edge-Frfox - content/newtab.css */
|
||||
|
||||
/*
|
||||
MARK: Default colours
|
||||
*/
|
||||
@-moz-document url-prefix("about:newtab"),
|
||||
url-prefix("about:home"),
|
||||
url-prefix("about:firefoxview") {
|
||||
:root:not([lwt-newtab], [lwt-newtab-brighttext]) {
|
||||
--newtab-background-color: #F7F7F7 !important;
|
||||
--newtab-background-color-secondary: #fff !important;
|
||||
--newtab-text-primary-color: #252525 !important;
|
||||
--newtab-primary-action-background: #0078D4 !important;
|
||||
--newtab-text-secondary-color: color-mix(in srgb, var(--newtab-text-primary-color) 80%, transparent) !important;
|
||||
--newtab-element-hover-color: color-mix(in srgb, var(--newtab-background-color) 95%, #000) !important;
|
||||
--newtab-element-active-color: color-mix(in srgb, var(--newtab-background-color) 90%, #000) !important;
|
||||
--newtab-element-secondary-color: color-mix(in srgb, currentColor 5%, transparent) !important;
|
||||
--newtab-element-secondary-hover-color: color-mix(in srgb, currentColor 12%, transparent) !important;
|
||||
--newtab-element-secondary-active-color: color-mix(in srgb, currentColor 25%, transparent) !important;
|
||||
--newtab-primary-element-hover-color: color-mix(in srgb, var(--newtab-primary-action-background) 90%, #000) !important;
|
||||
--newtab-primary-element-active-color: color-mix(in srgb, var(--newtab-primary-action-background) 80%, #000) !important;
|
||||
--newtab-primary-element-text-color: #fff !important;
|
||||
--newtab-primary-action-background-dimmed: color-mix(in srgb, var(--newtab-primary-action-background) 25%, transparent) !important;
|
||||
--newtab-border-color: color-mix(in srgb, var(--newtab-background-color) 75%, #000) !important;
|
||||
--newtab-wordmark-color: #737373 !important;
|
||||
--newtab-status-success: #50B080 !important;
|
||||
--newtab-status-error: #FF848A !important;
|
||||
--newtab-inner-box-shadow-color: rgb(0 0 0 / .1) !important;
|
||||
--newtab-overlay-color: color-mix(in srgb, var(--newtab-background-color) 85%, transparent) !important;
|
||||
--newtab-text-emphasis-background: #FFED32 !important;
|
||||
--newtab-text-emphasis-text-color: #000 !important;
|
||||
--newtab-textbox-focus-color: var(--newtab-primary-action-background) !important;
|
||||
--newtab-textbox-focus-boxshadow: 0 0 0 1px var(--newtab-primary-action-background), 0 0 0 4px rgba(var(--newtab-primary-action-background), 0.3) !important;
|
||||
--newtab-button-secondary-color: inherit !important;
|
||||
}
|
||||
|
||||
:root:not([style*="color"])[lwt-newtab-brighttext] {
|
||||
--newtab-background-color: #252525 !important;
|
||||
--newtab-background-color-secondary: #4A4A4A !important;
|
||||
--newtab-text-primary-color: #fff !important;
|
||||
--newtab-primary-action-background: #75B6E8 !important;
|
||||
--newtab-element-hover-color: color-mix(in srgb, var(--newtab-background-color) 95%, #FFF) !important;
|
||||
--newtab-element-active-color: color-mix(in srgb, var(--newtab-background-color) 90%, #FFF) !important;
|
||||
--newtab-element-secondary-color: color-mix(in srgb, currentColor 10%, transparent) !important;
|
||||
--newtab-element-secondary-hover-color: color-mix(in srgb, currentColor 17%, transparent) !important;
|
||||
--newtab-element-secondary-active-color: color-mix(in srgb, currentColor 30%, transparent) !important;
|
||||
--newtab-border-color: color-mix(in srgb, var(--newtab-background-color) 75%, #FFF) !important;
|
||||
--newtab-primary-element-text-color: #252525 !important;
|
||||
--newtab-wordmark-color: #fff !important;
|
||||
--newtab-status-success: #50B080 !important;
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Custom GTK colours
|
||||
*/
|
||||
@media (-moz-gtk-csd-available) {
|
||||
:root:not([lwt-newtab]):not([style*="color"]) {
|
||||
/* Colour adjusted for readability. */
|
||||
--uc-accent-adjusted: light-dark(color-mix(in oklch, AccentColor 92%, black), color-mix(in oklch, AccentColor 60%, white));
|
||||
--newtab-background-color: light-dark(-moz-dialog, color-mix(in srgb, -moz-dialog 82%, black)) !important;
|
||||
--newtab-background-color-secondary: light-dark(Field, color-mix(in srgb, -moz-dialog 86%, white)) !important;
|
||||
--newtab-text-primary-color: -moz-dialogtext !important;
|
||||
--newtab-wordmark-color: light-dark(color-mix(in srgb, -moz-dialogtext 65%, transparent), -moz-dialogtext) !important;
|
||||
--newtab-primary-action-background: var(--uc-accent-adjusted) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjustments for themes. */
|
||||
:root[style*="--newtab-background-color"] {
|
||||
/* Make the Firefox logo workmark follow text colour. */
|
||||
--newtab-wordmark-color: color-mix(in srgb, var(--newtab-text-primary-color) 60%, var(--newtab-background-color)) !important;
|
||||
|
||||
/* If there is no secondary background, create one from other colours. */
|
||||
&:not([style*="--newtab-background-color-secondary"]) {
|
||||
--newtab-background-color-secondary: color-mix(in srgb, var(--newtab-background-color) 15%, white) !important;
|
||||
}
|
||||
|
||||
/* Dark adjustments for themes. */
|
||||
&[lwt-newtab-brighttext] {
|
||||
/* Make the Firefox logo workmark follow text colour. */
|
||||
--newtab-wordmark-color: var(--newtab-text-primary-color) !important;
|
||||
|
||||
/* If there is no secondary background, create one from other colours. */
|
||||
&:not([style*="--newtab-background-color-secondary"]) {
|
||||
--newtab-background-color-secondary: color-mix(in srgb, var(--newtab-text-primary-color) 15%, var(--newtab-background-color)) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: New tab page
|
||||
*/
|
||||
@-moz-document url-prefix("about:newtab"),
|
||||
url-prefix("about:home") {
|
||||
/* Enforce system font selection. */
|
||||
:where(body) {
|
||||
font-family: system-ui !important;
|
||||
}
|
||||
|
||||
/* Hide pin icons. */
|
||||
.icon.icon-pin-small {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Unpinned sites have lower opacity titles. */
|
||||
.top-site-button .title:not(.pinned) {
|
||||
opacity: 0.7 !important;
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Search bar
|
||||
*/
|
||||
.search-wrapper {
|
||||
& .search-handoff-button {
|
||||
border-radius: 99px !important;
|
||||
background-position: 12px !important;
|
||||
padding-inline-start: 48px !important;
|
||||
white-space: nowrap !important;
|
||||
text-overflow: ellipsis !important;
|
||||
--elevation: 4;
|
||||
box-shadow:
|
||||
0 0 calc((var(--elevation) * 0.225px) + 2px) rgb(0 0 0 / .11),
|
||||
0 calc(var(--elevation) * 0.4px) calc((var(--elevation) * 0.9px)) rgb(0 0 0 / .13) !important;
|
||||
|
||||
&:hover {
|
||||
--elevation: 6;
|
||||
}
|
||||
}
|
||||
|
||||
& .search-inner-wrapper {
|
||||
min-height: 44px !important;
|
||||
}
|
||||
|
||||
&.fake-focus:not(.search.disabled) .search-handoff-button {
|
||||
border-color: transparent !important;
|
||||
outline: 2px solid var(--newtab-primary-action-background) !important;
|
||||
outline-offset: -1px !important;
|
||||
}
|
||||
|
||||
&.search-disabled .search-handoff-button {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
.search-handoff-button .fake-caret {
|
||||
inset-inline-start: 48px !important;
|
||||
top: 13px !important;
|
||||
}
|
||||
|
||||
@media (height > 700px) {
|
||||
.fixed-search & {
|
||||
padding: 15px 0 !important;
|
||||
min-height: auto !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fake-textbox {
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
}
|
||||
|
||||
.outer-wrapper.only-search {
|
||||
display: flex !important;
|
||||
padding: 30px !important;
|
||||
}
|
||||
|
||||
/* Sticky search bar header when scrolling. */
|
||||
@media (height > 700px) {
|
||||
body:not(.inline-onboarding) .fixed-search.visible-logo main {
|
||||
padding-top: 246px !important;
|
||||
|
||||
@media (-moz-bool-pref: "uc.tweak.hide-newtab-logo") {
|
||||
padding-top: 82px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-settings {
|
||||
background-image: url("../icons/settings.svg") !important;
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Top sites tiles
|
||||
*/
|
||||
.top-site-outer {
|
||||
& .tile {
|
||||
border-radius: 12px !important;
|
||||
height: 44px !important;
|
||||
width: 44px !important;
|
||||
box-shadow: rgb(0 0 0 / 0.1) 0px 2px 4px !important;
|
||||
|
||||
& .icon-wrapper {
|
||||
border-radius: 4px !important;
|
||||
width: 24px !important;
|
||||
height: 24px !important;
|
||||
|
||||
&.letter-fallback::before {
|
||||
font-size: 32px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .default-icon,
|
||||
& .search-topsite {
|
||||
background-size: 16px !important;
|
||||
height: 24px !important;
|
||||
width: 24px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Recent activity cards
|
||||
*/
|
||||
.card-outer {
|
||||
--uc-border-radius: 8px;
|
||||
|
||||
&,
|
||||
& .card,
|
||||
& .ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
|
||||
border-radius: var(--uc-border-radius) !important;
|
||||
}
|
||||
|
||||
& .card,
|
||||
& .ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) {
|
||||
box-shadow:
|
||||
rgb(0 0 0 / 0.14) 0px 1px 2px,
|
||||
rgb(0 0 0 / 0.12) 0px 0px 2px !important;
|
||||
}
|
||||
|
||||
& .card-preview-image-outer,
|
||||
& .ds-card-grid.ds-card-grid-border .ds-card:not(.placeholder) .img-wrapper .img img {
|
||||
border-radius: var(--uc-border-radius) var(--uc-border-radius) 0 0 !important
|
||||
}
|
||||
}
|
||||
|
||||
/* Add background & adjust padding for Recent activity & Pocket section. */
|
||||
.collapsible-section:not(.top-sites) {
|
||||
background-color: var(--newtab-background-color) !important;
|
||||
border-radius: 12px !important;
|
||||
padding: 10px 20px 20px !important;
|
||||
|
||||
& .section-list {
|
||||
gap: 12px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (-moz-bool-pref: "uc.tweak.hide-newtab-logo") {
|
||||
.logo-and-wordmark {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.outer-wrapper:not(.fixed-search) .search-wrapper {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Private browsing
|
||||
*/
|
||||
@-moz-document url-prefix(about:privatebrowsing) {
|
||||
:root {
|
||||
--uc-wordmark-color: light-dark(#737373, #fff);
|
||||
--uc-background-color-secondary: light-dark(#fff, #4a4a4a);
|
||||
|
||||
@media (-moz-bool-pref: "browser.theme.dark-private-windows") {
|
||||
color-scheme: dark !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom GTK colours. */
|
||||
@media (-moz-gtk-csd-available) {
|
||||
:root:not([lwt-newtab]):not([style*="color"]) {
|
||||
/* Colour adjusted for readability. */
|
||||
--uc-accent-adjusted: light-dark(color-mix(in oklch, AccentColor 92%, black), color-mix(in oklch, AccentColor 60%, white));
|
||||
--in-content-page-background: light-dark(-moz-dialog, color-mix(in srgb, -moz-dialog 82%, black)) !important;
|
||||
--uc-background-color-secondary: light-dark(Field, color-mix(in srgb, -moz-dialog 86%, white)) !important;
|
||||
--in-content-page-color: -moz-dialogtext !important;
|
||||
--in-content-focus-outline-color: var(--uc-accent-adjusted) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Search bar
|
||||
*/
|
||||
.search-inner-wrapper {
|
||||
height: 44px !important;
|
||||
|
||||
.search-handoff-button,
|
||||
.search-handoff-button:active,
|
||||
.search-handoff-button:enabled:hover:active {
|
||||
background-color: var(--uc-background-color-secondary) !important;
|
||||
border: 0 !important;
|
||||
border-radius: 99px !important;
|
||||
padding-inline-start: 48px !important;
|
||||
--elevation: 4;
|
||||
box-shadow:
|
||||
0 0 calc((var(--elevation) * 0.225px) + 2px) rgb(0 0 0 / .11),
|
||||
0 calc(var(--elevation) * 0.4px) calc((var(--elevation) * 0.9px)) rgb(0 0 0 / .13) !important;
|
||||
|
||||
&:hover {
|
||||
--elevation: 6;
|
||||
}
|
||||
|
||||
&.focused:not(.disabled) {
|
||||
box-shadow: none !important;
|
||||
outline: 2px solid var(--in-content-focus-outline-color) !important;
|
||||
outline-offset: -1px !important;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
& .fake-textbox {
|
||||
color: var(--in-content-page-color) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wordmark {
|
||||
fill: var(--uc-wordmark-color) !important;
|
||||
}
|
||||
|
||||
/* Information box in private browsing. */
|
||||
.info {
|
||||
background-color: var(--uc-background-color-secondary) !important;
|
||||
background-image: none !important;
|
||||
position: relative !important;
|
||||
|
||||
/* Private browsing icon. */
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 32px;
|
||||
top: 20px;
|
||||
height: 32px;
|
||||
aspect-ratio: 1 / 1;
|
||||
background: center / contain url("../icons/account-private.svg");
|
||||
border-radius: 99px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Background images
|
||||
*/
|
||||
@media (-moz-bool-pref: "uc.tweak.newtab-background") {
|
||||
@-moz-document url(about:newtab),
|
||||
url(about:home),
|
||||
url(about:privatebrowsing) {
|
||||
body {
|
||||
/* Specify both jpg and png file extensions, but jpg images will always
|
||||
* have a higher priority. */
|
||||
--bg-0: url(../background-0.jpg), url(../background-0.png);
|
||||
--bg-1: url(../background-1.jpg), url(../background-1.png);
|
||||
--newtab-element-hover-color: rgb(239 239 239 / .3) !important;
|
||||
--newtab-element-active-color: rgb(239 239 239 / .45) !important;
|
||||
--text-shadow: 0 1px 2px #000;
|
||||
--icon-shadow: drop-shadow(0 0.3px 0.6px #000);
|
||||
background-color: #252525 !important;
|
||||
background-image: var(--bg-0) !important;
|
||||
background-position: center !important;
|
||||
background-size: cover !important;
|
||||
background-attachment: fixed !important;
|
||||
|
||||
:root[lwt-newtab-brighttext] &,
|
||||
:root.private & {
|
||||
background-image: var(--bg-1), var(--bg-0) !important;
|
||||
--newtab-element-hover-color: rgb(66 66 66 / .4) !important;
|
||||
--newtab-element-active-color: rgb(66 66 66 / .55) !important;
|
||||
}
|
||||
|
||||
@media not (-moz-bool-pref: "browser.theme.dark-private-windows") {
|
||||
:root.private & {
|
||||
background-image: var(--bg-0) !important;
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-image: var(--bg-1), var(--bg-0) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Change text colour & add backgrounds to text over the background. */
|
||||
.top-site-outer .title {
|
||||
text-shadow: var(--text-shadow) !important;
|
||||
color: #fff !important;
|
||||
-moz-osx-font-smoothing: auto !important;
|
||||
}
|
||||
|
||||
.wordmark,
|
||||
.icon-settings,
|
||||
.top-site-outer .context-menu-button {
|
||||
filter: var(--icon-shadow) !important;
|
||||
color: #fff !important;
|
||||
fill: currentColor !important
|
||||
}
|
||||
|
||||
.top-site-outer .title .sponsored-label {
|
||||
color: #eee !important;
|
||||
}
|
||||
|
||||
/* Add transparent sticky search header. */
|
||||
@media (height > 700px) {
|
||||
.fixed-search .search-wrapper {
|
||||
border-bottom: 0 !important;
|
||||
background: transparent linear-gradient(#0004, transparent) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/* Local overrides.
|
||||
*
|
||||
* userChrome.css imports this last, so anything here wins over Edge-Frfox
|
||||
* without editing its files -- which keeps updating the theme a matter of
|
||||
* replacing the directory rather than merging into it.
|
||||
*
|
||||
* Deliberately empty. The import resolving to nothing is not an error in CSS,
|
||||
* but a file that exists is a seam somebody can find. */
|
||||
@@ -0,0 +1,206 @@
|
||||
/* Edge-Frfox - browser.css
|
||||
* https://github.com/bmFtZQ/edge-frfox */
|
||||
|
||||
/* Windows has 1px missing space on top of window, but not when accent colour is
|
||||
* applied to the title bar. */
|
||||
@media (-moz-platform: windows) and (not (-moz-windows-accent-color-in-titlebar)) {
|
||||
:root[sizemode="normal"][tabsintitlebar]:not([inFullscreen]) #navigator-toolbox {
|
||||
padding-top: 1px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a shadow when the toolbars move over content, eg. in macOS hovering over
|
||||
* the menu bar in fullscreen mode. */
|
||||
#navigator-toolbox[style*="transform"] {
|
||||
box-shadow: 0 -3px 5px 3px light-dark(rgb(0 0 0 / .3), rgb(0 0 0 / .6)) !important;
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Rounded corners (Rc)
|
||||
*/
|
||||
@media (-moz-bool-pref: "uc.tweak.rounded-corners") {
|
||||
/* Since Firefox 120, when the bookmarks bar is set to only open on new tabs,
|
||||
* the bookmarks bar overlaps the #browser element. */
|
||||
|
||||
/* Change the element that shifts the browser content to the correct position. */
|
||||
#main-window[BookmarksToolbarOverlapsBrowser] .newTabBrowserPanel,
|
||||
#main-window[BookmarksToolbarOverlapsBrowser] #sidebar-box {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
#main-window[BookmarksToolbarOverlapsBrowser] #browser {
|
||||
margin-top: var(--bookmarks-toolbar-overlapping-browser-height);
|
||||
}
|
||||
|
||||
/* Make sure that the toolbars are not positioned in-front of the browser
|
||||
* element. */
|
||||
#main-window[BookmarksToolbarOverlapsBrowser] #navigator-toolbox:not([style*="z-index"]) {
|
||||
z-index: auto !important;
|
||||
}
|
||||
|
||||
/* Move the browser element in-front of the toolbars when on the new tab page. */
|
||||
#main-window:not([BookmarksToolbarOverlapsBrowser]) #browser {
|
||||
z-index: 1 !important;
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Rc Variables
|
||||
*/
|
||||
#main-window:not([chromehidden~="toolbar"]) {
|
||||
--uc-tweak-rounded-corners-padding: 4px;
|
||||
--uc-tweak-rounded-corners-radius: 8px;
|
||||
--uc-tweak-rounded-corners-shadow:
|
||||
0 -.8px 0 0 rgb(0 0 0 / 0.02),
|
||||
0 0.5px 1px 1px rgb(0 0 0 / 0.06),
|
||||
0 1px 1px rgb(0 0 0 / 0.1);
|
||||
--browser-frame-bgcolor: var(--toolbar-bgcolor, var(--lwt-accent-color));
|
||||
|
||||
/* Different shadow for higher resolution displays. */
|
||||
@media (resolution > 1.8dppx) {
|
||||
--uc-tweak-rounded-corners-shadow:
|
||||
0 0 0.5px 0.5px rgb(0 0 0 / 0.06),
|
||||
0 0.5px 1.5px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
@media (-moz-bool-pref: "uc.tweak.floating-tabs") {
|
||||
--browser-frame-bgcolor: var(--lwt-accent-color);
|
||||
|
||||
&:-moz-window-inactive {
|
||||
--browser-frame-bgcolor: var(--lwt-accent-color-inactive, var(--lwt-accent-color));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide border below titlebar on macOS, as its titlebar already has a border. */
|
||||
@media (-moz-platform: macos) {
|
||||
:root[chromehidden~="toolbar"][chromehidden~="location"][chromehidden~="directories"] {
|
||||
border-top: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove separator between toolbars and the browser window. */
|
||||
#navigator-toolbox {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
/* Disable rounded corners in fullscreen, only if the toolbar is hidden. */
|
||||
:root[inFullscreen] #navigator-toolbox[style*="margin-top"] + #browser,
|
||||
:root[inDOMFullscreen] #browser {
|
||||
--uc-tweak-rounded-corners-padding: 0;
|
||||
--uc-tweak-rounded-corners-radius: 0;
|
||||
--uc-tweak-rounded-corners-shadow: unset;
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Rc Browser
|
||||
*/
|
||||
#appcontent .browserStack {
|
||||
margin-inline: var(--uc-tweak-rounded-corners-padding) !important;
|
||||
margin-block-end: var(--uc-tweak-rounded-corners-padding) !important;
|
||||
border-radius: var(--uc-tweak-rounded-corners-radius) !important;
|
||||
box-shadow: var(--uc-tweak-rounded-corners-shadow) !important;
|
||||
background-color: var(--tabpanel-background-color) !important;
|
||||
position: relative !important;
|
||||
|
||||
/* Use pseudo element overlay to simulate rounded corners, similar to the
|
||||
* tab corners, fixes bug with backdrop-filter. */
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
/* Draw toolbar background on the negative corner space, then draw the
|
||||
* same shadow from the browser window on top. */
|
||||
box-shadow:
|
||||
var(--uc-tweak-rounded-corners-shadow, 0 0 transparent),
|
||||
0 0 0 16px var(--browser-frame-bgcolor) !important;
|
||||
clip-path: inset(0);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prevent status panel corners and shadow from appearing outside the browser. */
|
||||
#statuspanel {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Rc Sidebar
|
||||
*/
|
||||
#sidebar-box {
|
||||
margin-inline-start: var(--uc-tweak-rounded-corners-padding) !important;
|
||||
margin-block-end: var(--uc-tweak-rounded-corners-padding) !important;
|
||||
border-radius: var(--uc-tweak-rounded-corners-radius) !important;
|
||||
overflow: hidden !important;
|
||||
box-shadow: var(--uc-tweak-rounded-corners-shadow) !important;
|
||||
|
||||
&[positionend] {
|
||||
margin-inline-start: 0 !important;
|
||||
margin-inline-end: var(--uc-tweak-rounded-corners-padding) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Rc Devtools
|
||||
*/
|
||||
|
||||
/* Devtools window when docked to the left or right. */
|
||||
.devtools-toolbox-side-iframe {
|
||||
margin-block-end: var(--uc-tweak-rounded-corners-padding) !important;
|
||||
border-radius: var(--uc-tweak-rounded-corners-radius) !important;
|
||||
overflow: hidden !important;
|
||||
box-shadow: var(--uc-tweak-rounded-corners-shadow) !important;
|
||||
|
||||
/* Devtools window when docked to the left. */
|
||||
&:first-child {
|
||||
margin-inline-start: var(--uc-tweak-rounded-corners-padding) !important;
|
||||
}
|
||||
|
||||
/* Devtools window when docked to the right. */
|
||||
&:last-child {
|
||||
margin-inline-end: var(--uc-tweak-rounded-corners-padding) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Devtools window when docked to the bottom. */
|
||||
.devtools-toolbox-bottom-iframe {
|
||||
margin: var(--uc-tweak-rounded-corners-padding) !important;
|
||||
margin-block-start: 0 !important;
|
||||
border-radius: var(--uc-tweak-rounded-corners-radius) !important;
|
||||
overflow: hidden !important;
|
||||
box-shadow: var(--uc-tweak-rounded-corners-shadow) !important;
|
||||
}
|
||||
|
||||
/* Hide devtools splitter. */
|
||||
.devtools-side-splitter,
|
||||
.devtools-horizontal-splitter {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
/* Responsive design mode. */
|
||||
.browserContainer.responsive-mode {
|
||||
background-color: transparent !important;
|
||||
|
||||
& .rdm-toolbar {
|
||||
margin-inline: var(--uc-tweak-rounded-corners-padding) !important;
|
||||
border-top-left-radius: var(--uc-tweak-rounded-corners-radius) !important;
|
||||
border-top-right-radius: var(--uc-tweak-rounded-corners-radius) !important;
|
||||
width: calc(100% - var(--uc-tweak-rounded-corners-padding) * 2) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove sidebar border when not in full screen. */
|
||||
:not(:root[inFullscreen] #navigator-toolbox[style*="margin-top"]) + #browser #sidebar-splitter {
|
||||
border-inline-width: 0 !important;
|
||||
}
|
||||
|
||||
/* Apply colour to the frame around the browser window. */
|
||||
#browser {
|
||||
background-color: var(--browser-frame-bgcolor);
|
||||
}
|
||||
|
||||
/* Change colour of page when loading */
|
||||
#tabbrowser-tabpanels {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
/* Edge-Frfox - colors.css
|
||||
* https://github.com/bmFtZQ/edge-frfox */
|
||||
|
||||
/*
|
||||
MARK: Default colours
|
||||
*/
|
||||
:root:not([lwtheme]),
|
||||
/* `--newtab-background-color-secondary used` to differentiate between a dark
|
||||
* private window and private window with the 'Dark' theme enabled.
|
||||
* May not be the most reliable method, but works for now up to FF 123. */
|
||||
:root[privatebrowsingmode="temporary"]:where([style*="--lwt-accent-color: rgb(28, 27, 34)"]:not([style*="--newtab-background-color-secondary: rgb(66, 65, 77)"])) {
|
||||
/* Toolbars */
|
||||
--lwt-accent-color: light-dark(#cecece, #1C1C1C) !important;
|
||||
--lwt-accent-color-inactive: light-dark(#e8e8e8, #2e2e2e) !important;
|
||||
--lwt-text-color: light-dark(#000, #FFF) !important;
|
||||
--lwt-text-color-inactive: light-dark(#3A3A3A, #CBCBCB) !important;
|
||||
--tab-selected-textcolor: light-dark(#000, #fff) !important;
|
||||
--tab-selected-textcolor-inactive: light-dark(#3E3E3E, #CBCBCB) !important;
|
||||
--tab-selected-bgcolor: var(--toolbar-bgcolor) !important;
|
||||
--tab-attention-icon-color: light-dark(#0078D4, #93d0ff) !important;
|
||||
--toolbar-bgcolor: light-dark(#f7f7f7, #3B3B3B) !important;
|
||||
--toolbar-non-lwt-bgcolor: var(--toolbar-bgcolor) !important;
|
||||
--toolbar-color: light-dark(#000, #fff) !important;
|
||||
--toolbarbutton-icon-fill: var(--toolbar-color) !important;
|
||||
--toolbarbutton-icon-fill-attention: light-dark(#0078D4, #4C98D1) !important;
|
||||
--toolbarseparator-color: color-mix(in srgb, currentColor 25%, transparent) !important;
|
||||
--chrome-content-separator-color: light-dark(#bfbfbf, #686868) !important;
|
||||
--newtab-background-color: light-dark(#f7f7f7, #252525) !important;
|
||||
--tabpanel-background-color: var(--newtab-background-color, var(--toolbar-bgcolor, transparent)) !important;
|
||||
/* Toolbar button overlays */
|
||||
--toolbarbutton-hover-background: color-mix(in srgb, currentColor 8%, transparent) !important;
|
||||
--toolbarbutton-active-background: color-mix(in srgb, currentColor 13.5%, transparent) !important;
|
||||
/* Tab audio indicator overlay icon */
|
||||
--tab-icon-overlay-fill: var(--toolbar-color) !important;
|
||||
--tab-icon-overlay-stroke: var(--toolbar-bgcolor) !important;
|
||||
/* URL bar */
|
||||
--toolbar-field-background-color: light-dark(#fff, #2B2B2B) !important;
|
||||
--toolbar-field-color: light-dark(#000, #fff) !important;
|
||||
--toolbar-field-border-color: transparent !important;
|
||||
--toolbar-field-focus-background-color: var(--toolbar-field-background-color) !important;
|
||||
--toolbar-field-focus-color: var(--toolbar-field-color) !important;
|
||||
--toolbar-field-focus-border-color: light-dark(#0055D780, #63ADE5) !important;
|
||||
--urlbar-icon-fill-opacity: 0.58 !important;
|
||||
--urlbar-box-bgcolor: light-dark(rgb(0 0 0 / .09), rgb(255 255 255 / .11)) !important;
|
||||
--urlbar-box-hover-bgcolor: light-dark(rgb(0 0 0 / .09), rgb(255 255 255 / .11)) !important;
|
||||
--urlbar-box-active-bgcolor: light-dark(rgb(0 0 0 / .22), rgb(255 255 255 / .28)) !important;
|
||||
--urlbar-box-focus-bgcolor: var(--urlbar-box-hover-bgcolor) !important;
|
||||
/* URL bar suggestions */
|
||||
--link-color: light-dark(#0078D4, #75B6E8) !important;
|
||||
--uc-urlbarView-accent-color: light-dark(#1A6397, #75B6E8) !important;
|
||||
--urlbarView-highlight-background: light-dark(#F2F2F2, #383838) !important;
|
||||
--urlbarView-highlight-color: light-dark(#000, #fff) !important;
|
||||
--urlbarView-hover-background: light-dark(#e5e5e5, #444444) !important;
|
||||
--urlbarView-separator-color: light-dark(#EFEFEF, #5F5F5F) !important;
|
||||
/* Menus/panels */
|
||||
--arrowpanel-background: light-dark(#fff, #4A4A4A) !important;
|
||||
--arrowpanel-color: light-dark(#1c1c1c, #fff) !important;
|
||||
--arrowpanel-border-color: light-dark(#fff, #636363) !important;
|
||||
--arrowpanel-dimmed: light-dark(#565656, #565656) !important;
|
||||
--arrowpanel-dimmed-further: light-dark(#707070, #707070) !important;
|
||||
--panel-separator-color: light-dark(#dadada, #636363) !important;
|
||||
--panel-description-color: light-dark(#000a, #fffb) !important;
|
||||
--panel-disabled-color: light-dark(#9E9E9E, #B9B9B9) !important;
|
||||
--panel-item-hover-bgcolor: light-dark(#eee, #646464) !important;
|
||||
--panel-item-active-bgcolor: light-dark(#ddd, #747474) !important;
|
||||
--panel-banner-item-update-supported-bgcolor: light-dark(#188038, #81C995) !important;
|
||||
/* Primary button */
|
||||
--button-primary-bgcolor: light-dark(#0078D4, #006CBE) !important;
|
||||
--button-primary-color: light-dark(#fff, #fff) !important;
|
||||
--button-primary-hover-bgcolor: light-dark(#006BBD, #1C7CC5) !important;
|
||||
--button-primary-active-bgcolor: light-dark(#0060AA, #3289CB) !important;
|
||||
/* Secondary button */
|
||||
--button-bgcolor: light-dark(#EFEFEF, #5C5C5C) !important;
|
||||
--button-color: light-dark(#000, #fff) !important;
|
||||
--button-hover-bgcolor: light-dark(#D5D5D5, #6E6E6E) !important;
|
||||
--button-active-bgcolor: light-dark(#C0C0C0, #7C7C7C) !important;
|
||||
/* Text input field, e.g. bookmarks editor */
|
||||
--focus-outline-color: light-dark(#0055D780, #63ADE5) !important;
|
||||
--input-bgcolor: light-dark(#fff, #4A4A4A) !important;
|
||||
--input-color: light-dark(#2b2b2b, #fff) !important;
|
||||
--input-border-color: light-dark(#bebebe, #808080) !important;
|
||||
--error-text-color: light-dark(#FF9AA2, #FF9AA2) !important;
|
||||
--input-error-border-color: light-dark(#FF848A, #FF848A) !important;
|
||||
/* Side bar colours. */
|
||||
--sidebar-background-color: light-dark(#f7f7f7, #3b3b3b) !important;
|
||||
--sidebar-text-color: light-dark(#000, #fff) !important;
|
||||
--sidebar-border-color: light-dark(#bfbfbf, #686868) !important;
|
||||
|
||||
/* ::::: Dialog theme ::::: */
|
||||
& #window-modal-dialog::backdrop {
|
||||
/* Overlay when dialog is shown. */
|
||||
background-color: rgb(20 20 20 / .2) !important;
|
||||
}
|
||||
|
||||
&[dialogroot] {
|
||||
/* Dialog */
|
||||
--in-content-page-background: var(--arrowpanel-background) !important;
|
||||
--in-content-page-color: var(--arrowpanel-color) !important;
|
||||
/* Primary button */
|
||||
--in-content-primary-button-text-color: var(--button-primary-color) !important;
|
||||
--in-content-primary-button-background: var(--button-primary-bgcolor) !important;
|
||||
--in-content-primary-button-background-hover: var(--button-primary-hover-bgcolor) !important;
|
||||
--in-content-primary-button-background-active: var(--button-primary-active-bgcolor) !important;
|
||||
/* Secondary button */
|
||||
--in-content-button-text-color: var(--button-color) !important;
|
||||
--in-content-button-background: var(--button-bgcolor) !important;
|
||||
--in-content-button-background-hover: var(--button-hover-bgcolor) !important;
|
||||
--in-content-button-background-active: var(--button-active-bgcolor) !important;
|
||||
/* Text input */
|
||||
--in-content-box-background: var(--input-bgcolor) !important;
|
||||
--in-content-box-border-color: var(--input-border-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure that dark mode is enabled completely in private browsing mode. */
|
||||
@media (-moz-bool-pref: "browser.theme.dark-private-windows") {
|
||||
:root[privatebrowsingmode="temporary"],
|
||||
:root[privatebrowsingmode="temporary"] #nav-bar,
|
||||
:root[privatebrowsingmode="temporary"] #PersonalToolbar {
|
||||
--toolbar-color-scheme: dark !important;
|
||||
--tab-selected-color-scheme: dark !important;
|
||||
color-scheme: dark !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply colours to the default 'Auto' theme. */
|
||||
:root:not([lwtheme]) #navigator-toolbox {
|
||||
background-color: var(--lwt-accent-color) !important;
|
||||
color: var(--lwt-text-color) !important;
|
||||
|
||||
/* Inactive window background colours. */
|
||||
&:-moz-window-inactive {
|
||||
background-color: var(--lwt-accent-color-inactive, var(--lwt-accent-color)) !important;
|
||||
color: var(--lwt-text-color-inactive, var(--lwt-text-color)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure that the tab toolbar opacity is always 1, inactive window colours
|
||||
* are already supplied by this theme. */
|
||||
:root[tabsintitlebar] #titlebar:-moz-window-inactive {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
/* Make the titlebar buttons black/white on default themes */
|
||||
:root:not([lwtheme])[tabsintitlebar] .titlebar-buttonbox {
|
||||
color: var(--lwt-text-color) !important;
|
||||
}
|
||||
|
||||
/* Status/link preview panel */
|
||||
#statuspanel-label {
|
||||
background-color: var(--toolbar-field-focus-background-color) !important;
|
||||
color: var(--toolbar-field-focus-color) !important;
|
||||
border-color: transparent !important;
|
||||
box-shadow: 0 2px 5px #0002, 0 0 0 1px light-dark(#0001, #0002) !important;
|
||||
border-radius: 0 4px 0 0 !important;
|
||||
|
||||
#statuspanel[mirror] & {
|
||||
border-radius: 4px 0 0 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Windows
|
||||
*/
|
||||
@media (-moz-platform: windows) {
|
||||
/* Colour the text selection background and foreground. */
|
||||
::selection {
|
||||
background-color: light-dark(#0078D4, #93B8E7) !important;
|
||||
color: light-dark(#fff, #000) !important;
|
||||
}
|
||||
|
||||
tooltip {
|
||||
background-color: var(--arrowpanel-background) !important;
|
||||
color: var(--arrowpanel-color) !important;
|
||||
border-color: var(--arrowpanel-border-color) !important;
|
||||
}
|
||||
|
||||
/* Use accent colour in titlebar when Windows option is enabled. */
|
||||
@media (-moz-windows-accent-color-in-titlebar) and (-moz-bool-pref: "browser.theme.windows.accent-color-in-tabs.enabled") {
|
||||
/* Uses :root:root to beat specificity of floating tabs adjustments. */
|
||||
:root:root:not([lwtheme]) {
|
||||
--lwt-accent-color: ActiveCaption !important;
|
||||
--lwt-text-color: CaptionText !important;
|
||||
--lwt-accent-color-inactive: InActiveCaption !important;
|
||||
--lwt-text-color-inactive: InActiveCaptionText !important;
|
||||
}
|
||||
}
|
||||
|
||||
.titlebar-button:not(.titlebar-close):hover {
|
||||
background-color: color-mix(in srgb, currentColor 11%, transparent) !important;
|
||||
|
||||
&:active {
|
||||
background-color: color-mix(in srgb, currentColor 20%, transparent) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.titlebar-close:hover {
|
||||
stroke: white !important;
|
||||
background-color: #e81123 !important;
|
||||
|
||||
&:active {
|
||||
background-color: #DC5C66 !important;
|
||||
}
|
||||
|
||||
toolbar[brighttext] &:active {
|
||||
background-color: #971821 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Library window colour theme. */
|
||||
window#places {
|
||||
--organizer-color: light-dark(#000, #fff) !important;
|
||||
--organizer-deemphasized-color: light-dark(#666, #bbb) !important;
|
||||
--organizer-toolbar-background: light-dark(#f7f7f7, #3B3B3B) !important;
|
||||
--organizer-pane-background: light-dark(#f7f7f7, #2B2B2B) !important;
|
||||
--organizer-content-background: light-dark(#f7f7f7, #1C1C1C) !important;
|
||||
--organizer-hover-background: light-dark(#00000025, #ffffff25) !important;
|
||||
--organizer-selected-background: light-dark(#006CBE80, #006CBE80) !important;
|
||||
--organizer-outline-color: light-dark(#0055D7a0, #63ADE5) !important;
|
||||
--organizer-toolbar-field-background: light-dark(#fff, #2B2B2B) !important;
|
||||
--organizer-toolbar-field-background-focused: light-dark(#fff, #2B2B2B) !important;
|
||||
--organizer-border-color: light-dark(#bfbfbf, #686868) !important;
|
||||
scrollbar-color:
|
||||
light-dark(#0006, #fff6)
|
||||
light-dark(#f7f7f740, #2B2B2B40) !important;
|
||||
}
|
||||
|
||||
#placesViewsBox #downloadsListBox richlistitem[selected="true"] {
|
||||
background-color: light-dark(#006CBE, #006CBE) !important;
|
||||
color: light-dark(#fff, #fff) !important;
|
||||
}
|
||||
|
||||
#placeContentColumns :is(treecol, treecolpicker) {
|
||||
background-color: light-dark(#f7f7f7, #3B3B3B) !important;
|
||||
color: light-dark(#000, #fff) !important;
|
||||
border-inline-start: 1px solid light-dark(#bfbfbf, #686868) !important;
|
||||
box-shadow: inset 0 -1px light-dark(#bfbfbf, #686868) !important;
|
||||
appearance: none !important;
|
||||
}
|
||||
|
||||
#placeContentColumns :is(treecol, treecolpicker):where(:hover) {
|
||||
background-color: light-dark(#cecece, #4A4A4A) !important;
|
||||
color: light-dark(#000, #fff) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prevent macOS vibrancy appearance, instead using a solid background. */
|
||||
#sidebar-box {
|
||||
appearance: none !important;
|
||||
}
|
||||
|
||||
.sidebar-panel:not([lwt-sidebar]) {
|
||||
color: light-dark(#000, #fff) !important;
|
||||
scrollbar-color: light-dark(#0006, #fff6) light-dark(#f7f7f740, #3b3b3b40) !important;
|
||||
|
||||
/* Sidebar search box (Windows) */
|
||||
@media(-moz-platform: windows) {
|
||||
& #sidebar-search-container > #search-box,
|
||||
& #viewButton {
|
||||
appearance: none !important;
|
||||
background-color: var(--toolbar-field-background-color) !important;
|
||||
color: inherit !important;
|
||||
border: 1px solid var(--input-border-color) !important;
|
||||
border-radius: 4px !important;
|
||||
padding: 6px 8px !important;
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid var(--input-border-color) !important;
|
||||
outline-offset: -2px !important;
|
||||
border-color: transparent !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-document url(chrome://browser/content/syncedtabs/sidebar.xhtml) {
|
||||
body {
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Linux / GTK csd
|
||||
*/
|
||||
@media (-moz-gtk-csd-available) {
|
||||
:root:not([lwtheme]),
|
||||
/* `--newtab-background-color-secondary used` to differentiate between a dark
|
||||
* private window and private window with the 'Dark' theme enabled.
|
||||
* May not be the most reliable method, but works for now up to FF 123. */
|
||||
:root[privatebrowsingmode="temporary"]:where([style*="--lwt-accent-color: rgb(28, 27, 34)"]:not([style*="--newtab-background-color-secondary: rgb(66, 65, 77)"])) {
|
||||
/* Used for mixing colours differently in light/dark themes. */
|
||||
--uc-tint: light-dark(black, white);
|
||||
/* Colour adjusted for readability. */
|
||||
--uc-accent-adjusted: light-dark(
|
||||
color-mix(in oklch, AccentColor 92%, black),
|
||||
color-mix(in oklch, AccentColor 60%, white)
|
||||
);
|
||||
/* Toolbars */
|
||||
--lwt-accent-color: light-dark(color-mix(in srgb, ActiveCaption 90%, black), ActiveCaption) !important;
|
||||
--lwt-accent-color-inactive: light-dark(color-mix(in srgb, InactiveCaption 94%, black), InactiveCaption) !important;
|
||||
--lwt-text-color: CaptionText !important;
|
||||
--lwt-text-color-inactive: color-mix(in srgb, CaptionText 65%, transparent) !important;
|
||||
--tab-selected-textcolor: var(--toolbar-color) !important;
|
||||
--tab-selected-textcolor-inactive: color-mix(in srgb, var(--toolbar-color) 65%, transparent) !important;
|
||||
--tab-selected-bgcolor: var(--toolbar-bgcolor) !important;
|
||||
--tab-attention-icon-color: var(--uc-accent-adjusted) !important;
|
||||
--toolbar-bgcolor: light-dark(-moz-dialog, color-mix(in srgb, -moz-dialog 92%, white)) !important;
|
||||
--toolbar-color: -moz-dialogtext !important;
|
||||
--toolbarbutton-icon-fill-attention: var(--uc-accent-adjusted) !important;
|
||||
--chrome-content-separator-color: color-mix(in srgb, currentColor 17%, var(--toolbar-bgcolor)) !important;
|
||||
--newtab-background-color: light-dark(-moz-dialog, color-mix(in srgb, -moz-dialog 82%, black)) !important;
|
||||
/* URL bar */
|
||||
--toolbar-field-background-color: light-dark(Field, color-mix(in srgb, Field 96%, -moz-dialogtext)) !important;
|
||||
--toolbar-field-color: FieldText !important;
|
||||
--toolbar-field-focus-border-color: light-dark(color-mix(in srgb, var(--focus-outline-color) 50%, transparent), var(--focus-outline-color)) !important;
|
||||
--urlbar-box-bgcolor: var(--button-bgcolor) !important;
|
||||
--urlbar-box-hover-bgcolor: var(--button-hover-bgcolor) !important;
|
||||
--urlbar-box-active-bgcolor: var(--button-active-bgcolor) !important;
|
||||
/* URL bar suggestions. */
|
||||
--link-color: var(--uc-accent-adjusted) !important;
|
||||
--uc-urlbarView-accent-color: var(--uc-accent-adjusted) !important;
|
||||
--urlbarView-highlight-background: var(--panel-item-hover-bgcolor) !important;
|
||||
--urlbarView-highlight-color: var(--toolbar-field-color) !important;
|
||||
--urlbarView-hover-background: var(--panel-item-active-bgcolor) !important;
|
||||
--urlbarView-separator-color: var(--panel-separator-color) !important;
|
||||
/* Menus/panels */
|
||||
--arrowpanel-background: Field !important;
|
||||
--arrowpanel-color: FieldText !important;
|
||||
--arrowpanel-border-color: light-dark(transparent, var(--panel-separator-color)) !important;
|
||||
--arrowpanel-dimmed: color-mix(in srgb, currentColor 17%, transparent) !important;
|
||||
--arrowpanel-dimmed-further: color-mix(in srgb, currentColor 30%, transparent) !important;
|
||||
--panel-separator-color: color-mix(in srgb, currentColor 17%, var(--arrowpanel-background)) !important;
|
||||
--panel-description-color: GrayText !important;
|
||||
--panel-disabled-color: GrayText !important;
|
||||
--panel-item-hover-bgcolor: color-mix(in srgb, currentColor 8%, transparent) !important;
|
||||
--panel-item-active-bgcolor: color-mix(in srgb, currentColor 13%, transparent) !important;
|
||||
/* Primary button */
|
||||
--button-primary-bgcolor: AccentColor !important;
|
||||
--button-primary-color: AccentColorText !important;
|
||||
--button-primary-hover-bgcolor: color-mix(in srgb, var(--uc-tint) 10%, var(--button-primary-bgcolor)) !important;
|
||||
--button-primary-active-bgcolor: color-mix(in srgb, var(--uc-tint) 20%, var(--button-primary-bgcolor)) !important;
|
||||
/* Secondary button */
|
||||
--button-bgcolor: color-mix(in srgb, currentColor 10%, transparent) !important;
|
||||
--button-color: currentColor !important;
|
||||
--button-hover-bgcolor: color-mix(in srgb, currentColor 14%, transparent) !important;
|
||||
--button-active-bgcolor: color-mix(in srgb, currentColor 20%, transparent) !important;
|
||||
/* Text input field, e.g. bookmarks editor. */
|
||||
--focus-outline-color: var(--uc-accent-adjusted) !important;
|
||||
--input-bgcolor: var(--arrowpanel-background) !important;
|
||||
--input-color: var(--arrowpanel-color) !important;
|
||||
--input-border-color: color-mix(in srgb, currentColor 35%, var(--arrowpanel-background)) !important;
|
||||
/* Sidebar */
|
||||
--sidebar-background-color: var(--toolbar-bgcolor) !important;
|
||||
--sidebar-text-color: var(--toolbar-color) !important;
|
||||
--sidebar-border-color: var(--chrome-content-separator-color) !important;
|
||||
}
|
||||
|
||||
/* Use the same sidebar splitter style as other platforms. */
|
||||
.sidebar-splitter {
|
||||
border: 0 solid var(--sidebar-border-color) !important;
|
||||
border-inline-end-width: 1px !important;
|
||||
width: 4px !important;
|
||||
background-color: transparent !important;
|
||||
margin-inline-start: -4px !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,847 @@
|
||||
/* Edge-Frfox - popup.css */
|
||||
|
||||
/*
|
||||
MARK: Variables
|
||||
*/
|
||||
:root {
|
||||
--panel-subview-body-padding: 2px 0 !important;
|
||||
--arrowpanel-border-radius: 8px !important;
|
||||
--arrowpanel-menuitem-border-radius: 4px !important;
|
||||
--arrowpanel-menuitem-margin: var(--uc-arrowpanel-menuitem-margin-block) var(--uc-arrowpanel-menuitem-margin-inline) !important;
|
||||
--arrowpanel-menuitem-padding-block: 6px !important;
|
||||
--arrowpanel-menuitem-padding-inline: 10px !important;
|
||||
--uc-arrowpanel-menuicon-margin-inline: 14px;
|
||||
--uc-arrowpanel-menuitem-margin-inline: 4px;
|
||||
--uc-arrowpanel-menuitem-margin-block: 2px;
|
||||
--panel-separator-margin-vertical: 2px !important;
|
||||
--panel-separator-margin-horizontal: 0 !important;
|
||||
|
||||
--uc-panel-zoom-button-padding: 8px;
|
||||
--uc-panel-zoom-button-inline-padding: 9px;
|
||||
--uc-panel-zoom-padding-block: calc(var(--panel-separator-margin-vertical) + var(--uc-arrowpanel-menuitem-margin-block));
|
||||
|
||||
--uc-autocomplete-panel-menuitem-margin: 4px;
|
||||
--uc-autocomplete-panel-menuicon-padding-inline: 14px;
|
||||
--uc-autocomplete-panel-separator-margin-vertical: 4px;
|
||||
|
||||
--uc-permission-itemcontainer-padding-block: 8px;
|
||||
--uc-permission-item-margin-block: 4px;
|
||||
--uc-permission-item-padding-inline: 16px;
|
||||
|
||||
--uc-contextmenu-border-radius: 8px;
|
||||
--uc-contextmenu-padding: calc(4px - var(--uc-contextmenu-menuitem-border-width)) 0;
|
||||
--uc-contextmenu-menuitem-border-radius: calc(4px + var(--uc-contextmenu-menuitem-border-width));
|
||||
--uc-contextmenu-menuitem-padding-block: 6px;
|
||||
--uc-contextmenu-menuitem-padding-inline: 10px;
|
||||
--uc-contextmenu-menuitem-border-width: 2px;
|
||||
--uc-contextmenu-menuicon-margin-inline: 12px;
|
||||
--uc-contextmenu-menuitem-margin-inline: calc(4px - var(--uc-contextmenu-menuitem-border-width));
|
||||
--uc-contextmenu-menuitem-margin-block: 0px;
|
||||
--uc-contextmenu-menuitem-margin: var(--uc-contextmenu-menuitem-margin-block) var(--uc-contextmenu-menuitem-margin-inline);
|
||||
--uc-contextmenu-separator-vertical: calc(4px - var(--uc-contextmenu-menuitem-border-width));
|
||||
--uc-contextmenu-separator-horizontal: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: App menu
|
||||
*/
|
||||
.addon-banner-item,
|
||||
.panel-banner-item {
|
||||
margin: 2px 4px 2px !important;
|
||||
padding-inline: 4px 12px !important;
|
||||
padding-block: var(--arrowpanel-menuitem-padding-block) !important;
|
||||
border-radius: var(--arrowpanel-menuitem-border-radius) !important;
|
||||
}
|
||||
|
||||
#appMenu-fxa-label2 label,
|
||||
#PanelUI-fxa-menu-syncnow-button label {
|
||||
margin-block: 0 !important;
|
||||
}
|
||||
|
||||
.widget-overflow-list .toolbarbutton-1:not(.toolbarbutton-combined) > .toolbarbutton-text,
|
||||
.subviewbutton:not(#appMenu-zoom-controls > .subviewbutton) > .toolbarbutton-icon + .toolbarbutton-text,
|
||||
#appMenu-fxa-label2 > vbox {
|
||||
padding-inline-start: var(--uc-arrowpanel-menuicon-margin-inline) !important;
|
||||
}
|
||||
|
||||
/* special case menuitems with no icons */
|
||||
#appMenu-zoom-controls > .toolbarbutton-text,
|
||||
#fxa-manage-account-button > vbox,
|
||||
#PanelUI-fxa-menu-syncnow-button > hbox {
|
||||
padding-inline-start: calc(16px + var(--uc-arrowpanel-menuicon-margin-inline)) !important;
|
||||
}
|
||||
|
||||
.subviewbutton > .toolbarbutton-icon {
|
||||
width: 16px !important;
|
||||
}
|
||||
|
||||
/* firefox profile avatar in appmenu */
|
||||
#appMenu-fxa-label2::before {
|
||||
content: "";
|
||||
display: flex;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
background: var(--avatar-image-url) 0/16px;
|
||||
scale: 1.25;
|
||||
border-radius: 99px;
|
||||
}
|
||||
|
||||
/* disable proton account separator */
|
||||
#appMenu-fxa-separator {
|
||||
border-image: none !important;
|
||||
}
|
||||
|
||||
#appMenu-fxa-status2:not([fxastatus]) {
|
||||
padding-block: 0 !important;
|
||||
}
|
||||
|
||||
#appMenu-fxa-status2:not([fxastatus]) > #appMenu-fxa-label2 {
|
||||
margin-inline-end: calc(var(--arrowpanel-menuitem-padding-inline) * -1) !important;
|
||||
}
|
||||
|
||||
/* zoom controls */
|
||||
#appMenu-zoom-controls {
|
||||
border-top: 1px solid var(--panel-separator-color) !important;
|
||||
padding-inline: calc(var(--arrowpanel-menuitem-padding-inline) + var(--uc-arrowpanel-menuitem-margin-inline)) var(--uc-arrowpanel-menuitem-margin-inline) !important;
|
||||
padding-block: var(--uc-panel-zoom-padding-block) !important;
|
||||
margin: var(--panel-separator-margin-vertical) 0 calc(var(--panel-separator-margin-vertical) * -1) !important;
|
||||
}
|
||||
|
||||
#appMenu-zoom-controls > .subviewbutton {
|
||||
padding: var(--uc-panel-zoom-button-padding) var(--uc-panel-zoom-button-inline-padding) !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
#appMenu-zoom-controls > #appMenu-zoomReset-button2 {
|
||||
padding: var(--uc-panel-zoom-button-padding) calc(var(--uc-panel-zoom-button-padding) / 2) !important;
|
||||
}
|
||||
|
||||
/* #appMenu-zoomReduce-button2, */
|
||||
#appMenu-zoom-controls > #appMenu-fullscreen-button2 {
|
||||
margin-left: calc((var(--panel-separator-margin-vertical) + var(--uc-arrowpanel-menuitem-margin-block)) * 2 + 1px) !important;
|
||||
}
|
||||
|
||||
#appMenu-zoom-controls > #appMenu-fullscreen-button2::before {
|
||||
content: "";
|
||||
border-inline-start: 1px solid var(--panel-separator-color);
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 32px;
|
||||
margin-block: calc(var(--uc-panel-zoom-button-padding) * -1);
|
||||
transform: translateX(calc(var(--uc-panel-zoom-button-inline-padding) * -1 - (var(--panel-separator-margin-vertical) + var(--uc-arrowpanel-menuitem-margin-block)) - 1px));
|
||||
}
|
||||
|
||||
#appMenu-zoomReset-button2 {
|
||||
height: calc(16px + var(--uc-panel-zoom-button-padding) * 2) !important;
|
||||
min-height: calc(16px + var(--uc-panel-zoom-button-padding) * 2) !important;
|
||||
}
|
||||
|
||||
#appMenu-zoomReduce-button2:not([disabled], [open], :active):is(:hover),
|
||||
#appMenu-zoomEnlarge-button2:not([disabled], [open], :active):is(:hover),
|
||||
#appMenu-fullscreen-button2:not([disabled], [open], :active):is(:hover),
|
||||
#appMenu-zoomReset-button2:not([disabled], [open], :active):is(:hover) {
|
||||
background-color: var(--panel-item-hover-bgcolor) !important;
|
||||
}
|
||||
|
||||
#appMenu-zoomReduce-button2:not([disabled]):is([open], :hover:active),
|
||||
#appMenu-zoomEnlarge-button2:not([disabled]):is([open], :hover:active),
|
||||
#appMenu-fullscreen-button2:not([disabled]):is([open], :hover:active),
|
||||
#appMenu-zoomReset-button2:not([disabled]):is([open], :hover:active) {
|
||||
background-color: var(--panel-item-active-bgcolor) !important;
|
||||
}
|
||||
|
||||
#appMenu-zoomReduce-button2 > .toolbarbutton-icon,
|
||||
#appMenu-zoomEnlarge-button2 > .toolbarbutton-icon {
|
||||
fill: currentColor !important;
|
||||
stroke: none !important;
|
||||
}
|
||||
|
||||
#appMenu-zoomReset-button2 > .toolbarbutton-text,
|
||||
#appMenu-fullscreen-button2 > .toolbarbutton-icon {
|
||||
background-color: transparent !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.subviewbutton[shortcut]::after {
|
||||
opacity: 0.7 !important;
|
||||
}
|
||||
|
||||
#widget-overflow-mainView .panel-subview-body {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.PanelUI-subView > .panel-header + toolbarseparator {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.PanelUI-subView > .panel-header + toolbarseparator + .panel-subview-body {
|
||||
padding-top: var(--panel-separator-margin-vertical) !important;
|
||||
}
|
||||
|
||||
#identity-popup-security-button {
|
||||
margin-bottom: var(--panel-separator-margin-vertical) !important;
|
||||
}
|
||||
|
||||
#permission-popup-mainView-panel-header,
|
||||
#identity-popup-mainView-panel-header,
|
||||
#protections-popup-mainView-panel-header,
|
||||
.panel-header {
|
||||
min-height: calc((var(--arrowpanel-menuitem-padding-block) + 4px) * 2 + 16px) !important;
|
||||
}
|
||||
|
||||
/* URL bar popup */
|
||||
.panel-footer > button {
|
||||
border-radius: 2px !important;
|
||||
}
|
||||
|
||||
.identity-popup-security-connection > hbox > description {
|
||||
margin-inline-start: 0 !important;
|
||||
}
|
||||
|
||||
.identity-popup-security-connection.identity-button {
|
||||
margin-inline-end: calc(-1 * (var(--arrowpanel-menuitem-padding-inline) - 10px)) !important;
|
||||
}
|
||||
|
||||
#identity-popup-mainView-panel-header-span,
|
||||
#permission-popup-mainView-panel-header-span,
|
||||
#identity-popup-mainView-panel-header label,
|
||||
#permission-popup-mainView-panel-header label,
|
||||
#protections-popup-mainView-panel-header-span {
|
||||
margin-block: 0 !important;
|
||||
}
|
||||
|
||||
.permission-popup-section {
|
||||
padding-block: var(--uc-permission-itemcontainer-padding-block) !important;
|
||||
}
|
||||
|
||||
#permission-popup-permissions-content {
|
||||
padding-inline: var(--uc-permission-item-padding-inline) !important;
|
||||
}
|
||||
|
||||
.permission-popup-permission-item,
|
||||
#permission-popup-storage-access-permission-list-header {
|
||||
margin-block: var(--uc-permission-item-margin-block) !important;
|
||||
}
|
||||
|
||||
.permission-popup-permission-label,
|
||||
.permission-popup-permission-header-label {
|
||||
margin-inline-start: var(--uc-arrowpanel-menuicon-margin-inline) !important;
|
||||
}
|
||||
|
||||
#editBookmarkPanel > #editBookmarkHeaderSeparator,
|
||||
#editBookmarkPanel > .panel-subview-body > #editBookmarkSeparator {
|
||||
margin-inline: 0 !important;
|
||||
}
|
||||
|
||||
/* subview */
|
||||
.panel-header {
|
||||
padding: 4px !important;
|
||||
padding-top: 4px !important;
|
||||
}
|
||||
|
||||
.panel-header + toolbarseparator,
|
||||
#identity-popup-mainView-panel-header + toolbarseparator,
|
||||
#permission-popup-mainView-panel-header + toolbarseparator,
|
||||
#protections-popup-mainView-panel-header-section + toolbarseparator {
|
||||
margin-block-start: 0 !important;
|
||||
}
|
||||
|
||||
.panel-header > .subviewbutton-back {
|
||||
margin: 0 !important;
|
||||
padding: var(--arrowpanel-menuitem-padding-block) !important;
|
||||
}
|
||||
|
||||
.panel-subview-footer-button {
|
||||
margin-bottom: 4px !important;
|
||||
}
|
||||
|
||||
.webextension-popup-browser,
|
||||
.webextension-popup-stack {
|
||||
margin-block-start: calc(var(--panel-separator-margin-vertical) * -.5) !important;
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Downloads
|
||||
*/
|
||||
.downloadTypeIcon {
|
||||
height: 16px !important;
|
||||
width: 16px !important;
|
||||
}
|
||||
|
||||
.downloadButton {
|
||||
min-width: calc(16px + var(--uc-toolbarbutton-inner-inline-padding) * 2) !important;
|
||||
height: calc(16px + var(--toolbarbutton-inner-padding) * 2) !important;
|
||||
border-radius: var(--toolbarbutton-border-radius) !important;
|
||||
}
|
||||
|
||||
.downloadProgress::-moz-progress-bar,
|
||||
.downloadProgress > div {
|
||||
background-color: var(--toolbarbutton-icon-fill-attention) !important;
|
||||
}
|
||||
|
||||
.downloadMainArea {
|
||||
padding-block: 4px !important;
|
||||
padding-inline: 12px !important;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
#downloadsListBox > richlistitem {
|
||||
padding-inline-end: 8px !important;
|
||||
border-radius: var(--arrowpanel-menuitem-border-radius) !important;
|
||||
}
|
||||
|
||||
#downloadsListBox,
|
||||
.downloadsPanelFooterButton {
|
||||
margin: var(--arrowpanel-menuitem-margin) !important;
|
||||
width: auto !important;
|
||||
min-width: auto !important;
|
||||
}
|
||||
|
||||
#downloadsPanel-blockedSubview,
|
||||
#downloadsPanel-mainView {
|
||||
padding: var(--panel-separator-margin-vertical) 0 !important;
|
||||
}
|
||||
|
||||
#emptyDownloads {
|
||||
padding: 1.5em 16px 1.7em !important;
|
||||
}
|
||||
|
||||
#downloadsHistory .button-text {
|
||||
margin-block: 0 !important;
|
||||
margin-inline: 0 !important;
|
||||
}
|
||||
|
||||
.PanelUI-subView .subviewbutton-nav::after,
|
||||
#PlacesToolbar menu > .menu-right,
|
||||
.subviewbutton > .menu-right {
|
||||
fill-opacity: 1 !important;
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Extensions
|
||||
*/
|
||||
/* #region */
|
||||
|
||||
.toolbaritem-combined-buttons:is(:not([cui-areatype="toolbar"]), [overflowedItem="true"]) > toolbarbutton:not(.toolbarbutton-1)[disabled] {
|
||||
opacity: 1 !important;
|
||||
color: var(--panel-disabled-color) !important;
|
||||
}
|
||||
|
||||
/* Removes space above extensions items. */
|
||||
#unified-extensions-messages-container {
|
||||
margin-block: 0 !important;
|
||||
}
|
||||
|
||||
:root {
|
||||
--uei-icon-size: 24px !important;
|
||||
}
|
||||
|
||||
@media (-moz-bool-pref: "uc.tweak.context-menu.compact-extensions-menu") {
|
||||
:root {
|
||||
--uei-icon-size: 16px !important;
|
||||
}
|
||||
|
||||
/* Hide site access permissions label. */
|
||||
.unified-extensions-item-message-deck {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Use smaller toolbar extension icon if available. */
|
||||
toolbaritem:is([overflowedItem="true"], [cui-areatype="panel"]) > .webextension-browser-action {
|
||||
list-style-image: var(--webextension-toolbar-image, var(--webextension-menupanel-image, inherit)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Use smaller font-size in extensions menu on Windows. */
|
||||
@media (-moz-platform: windows) {
|
||||
#unified-extensions-panel .toolbarbutton-text {
|
||||
font: menu !important;
|
||||
font-family: inherit !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prevent extension labels from using >1 line, adding ellipsis if necessary. */
|
||||
.unified-extensions-item-contents {
|
||||
flex: 1 !important;
|
||||
|
||||
& > .unified-extensions-item-name,
|
||||
& > .unified-extensions-item-message-deck > .unified-extensions-item-message {
|
||||
max-width: calc(100% - 4px) !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extension item menu button. */
|
||||
.unified-extensions-item-menu-button {
|
||||
padding: 0 !important;
|
||||
|
||||
& > .toolbarbutton-icon {
|
||||
border: none !important;
|
||||
padding-inline: var(--arrowpanel-menuitem-padding-inline) !important;
|
||||
padding-block: var(--arrowpanel-menuitem-padding-block) !important;
|
||||
margin-inline-start: var(--uc-arrowpanel-menuitem-margin-inline) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add grayscale filter to extensions with no access to current site. */
|
||||
.unified-extensions-list .unified-extensions-item-action-button:has(.unified-extensions-item-message[data-l10n-id="origin-controls-state-no-access"]) .unified-extensions-item-icon {
|
||||
filter: grayscale(1) !important;
|
||||
}
|
||||
|
||||
/* Adjust spacing between the extension item's icon and label. */
|
||||
.unified-extensions-item-name,
|
||||
.unified-extensions-item-message {
|
||||
padding-inline-start: var(--uc-arrowpanel-menuicon-margin-inline) !important;
|
||||
}
|
||||
|
||||
.unified-extensions-item-icon,
|
||||
.unified-extensions-item .webextension-browser-action > .toolbarbutton-badge-stack {
|
||||
margin-inline-end: 0 !important;
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
/*
|
||||
MARK: Autocomplete
|
||||
*/
|
||||
#PopupAutoComplete {
|
||||
/* workaround for inaccessible shadow-root element
|
||||
moves the shadow-root element underneath, and away from panel edges */
|
||||
padding-block: 10px !important;
|
||||
padding-inline: 2px !important;
|
||||
|
||||
& > .autocomplete-richlistbox {
|
||||
background: var(--arrowpanel-background) !important;
|
||||
box-shadow: 0 0 0 1px inset var(--arrowpanel-border-color) !important;
|
||||
height: auto !important;
|
||||
margin-block: -10px !important;
|
||||
margin-inline: -2px !important;
|
||||
padding: var(--uc-contextmenu-padding) !important;
|
||||
border-radius: var(--uc-contextmenu-border-radius) !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
& .autocomplete-richlistitem[originaltype="loginsFooter"] {
|
||||
justify-content: start !important;
|
||||
color: inherit !important;
|
||||
background-color: transparent !important;
|
||||
height: 28px !important;
|
||||
min-height: 28px !important;
|
||||
padding-inline-start: var(--uc-contextmenu-menuitem-padding-inline) !important;
|
||||
position: relative !important;
|
||||
overflow: visible !important;
|
||||
margin-top: calc(var(--uc-contextmenu-separator-vertical) * 2 + 1px) !important;
|
||||
}
|
||||
|
||||
/* Separator */
|
||||
& .autocomplete-richlistitem[originaltype="loginsFooter"]::before {
|
||||
content: "";
|
||||
width: calc(100% + var(--uc-contextmenu-menuitem-margin-inline) * 2);
|
||||
border-top: 1px solid var(--panel-separator-color);
|
||||
position: absolute;
|
||||
top: calc((var(--uc-contextmenu-separator-vertical) + var(--uc-contextmenu-menuitem-border-width)) * -1 - 1px);
|
||||
left: calc(var(--uc-contextmenu-menuitem-margin-inline) * -1);
|
||||
}
|
||||
|
||||
& .autocomplete-richlistitem:hover {
|
||||
background-color: var(--panel-item-hover-bgcolor) !important;
|
||||
}
|
||||
|
||||
& .autocomplete-richlistitem[selected] {
|
||||
background-color: var(--panel-item-active-bgcolor) !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
& .ac-site-icon {
|
||||
fill: currentColor !important;
|
||||
margin-block: auto !important;
|
||||
margin-inline: 0 var(--uc-autocomplete-panel-menuicon-padding-inline) !important;
|
||||
}
|
||||
|
||||
& .ac-title {
|
||||
margin-inline: calc(16px + var(--uc-autocomplete-panel-menuicon-padding-inline)) !important;
|
||||
}
|
||||
|
||||
& .two-line-wrapper {
|
||||
padding: 0 !important;
|
||||
gap: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
#PlacesToolbar menuitem,
|
||||
#PlacesToolbar menu,
|
||||
panelview .toolbarbutton-1,
|
||||
.subviewbutton,
|
||||
.widget-overflow-list .toolbarbutton-1 {
|
||||
min-height: calc(16px + var(--uc-contextmenu-menuitem-padding-block) * 2) !important;
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Search bar suggestions
|
||||
*/
|
||||
#PopupSearchAutoComplete {
|
||||
& .searchbar-separator {
|
||||
margin: 4px 0 !important;
|
||||
}
|
||||
|
||||
/* Search suggestions. */
|
||||
& .autocomplete-richlistitem,
|
||||
& .search-panel-header {
|
||||
padding-block: 2px !important;
|
||||
padding-inline: var(--uc-contextmenu-menuitem-padding-inline) !important;
|
||||
margin: 0 var(--uc-arrowpanel-menuitem-margin-inline) !important;
|
||||
border-radius: var(--arrowpanel-menuitem-border-radius) !important;
|
||||
|
||||
& > .ac-type-icon {
|
||||
margin-inline-start: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Search engine selector. */
|
||||
& .search-one-offs {
|
||||
padding-block: 0 5px !important;
|
||||
|
||||
& #searchbar-one-offs-header-label {
|
||||
margin-inline-start: 0 !important;
|
||||
}
|
||||
|
||||
& .search-panel-one-offs-container {
|
||||
margin-inline: 1px 7px !important;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Context menu
|
||||
*/
|
||||
|
||||
/* disable context menu when this tweak is applied */
|
||||
@media not (-moz-bool-pref: "uc.tweak.revert-context-menu") {
|
||||
/* Context Menu */
|
||||
menupopup:not(.PanelUI-subView),
|
||||
menupopup:not(.PanelUI-subView) menuitem,
|
||||
menupopup:not(.PanelUI-subView) menu,
|
||||
menupopup:not(.PanelUI-subView) menuseparator,
|
||||
panel:not(#PopupSearchAutoComplete) {
|
||||
appearance: none !important;
|
||||
}
|
||||
|
||||
menupopup:not(.PanelUI-subView),
|
||||
#PopupAutoComplete,
|
||||
menulist > menupopup:not(.PanelUI-subView) > menucaption {
|
||||
-moz-window-shadow: none !important;
|
||||
background: 0 !important;
|
||||
border: 0 !important;
|
||||
color: var(--arrowpanel-color) !important;
|
||||
}
|
||||
|
||||
.popup-internal-box,
|
||||
#PopupAutoComplete > .autocomplete-richlistbox,
|
||||
.menupopup-arrowscrollbox {
|
||||
background: var(--arrowpanel-background) !important;
|
||||
border: 1px solid var(--arrowpanel-border-color) !important;
|
||||
}
|
||||
|
||||
/* make room for menu shadow */
|
||||
menupopup:not(.PanelUI-subView) {
|
||||
padding: 4px 12px 20px !important;
|
||||
margin: -6px -12px -16px !important;
|
||||
}
|
||||
|
||||
/* cut off shadow so it doesn't get in the way of the menubar items */
|
||||
menubar menupopup {
|
||||
padding: 1px 8px 20px !important;
|
||||
margin: -1px -4px -16px !important;
|
||||
}
|
||||
|
||||
menupopup {
|
||||
--uc-margin: 0 !important;
|
||||
--uc-outline-color: #0001;
|
||||
--uc-box-shadow:
|
||||
0 8px 12px #0003,
|
||||
0 4px 5px #0002,
|
||||
0 2px 4px #0002;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme:dark) {
|
||||
menupopup {
|
||||
--uc-outline-color: #0002;
|
||||
--uc-box-shadow:
|
||||
0 8px 12px #0003,
|
||||
0 4px 5px #0003,
|
||||
0 2px 4px #0002;
|
||||
}
|
||||
}
|
||||
|
||||
/* use default shadow for subview menupopup (eg. bookmarks) */
|
||||
menupopup.PanelUI-subView {
|
||||
--uc-box-shadow: 0 0 0 1px var(--panel-border-color);
|
||||
--uc-margin: 1px !important;
|
||||
margin-top: -5px !important;
|
||||
}
|
||||
|
||||
.menupopup-arrowscrollbox {
|
||||
box-shadow: var(--uc-box-shadow) !important;
|
||||
outline: .5px solid var(--uc-outline-color) !important;
|
||||
}
|
||||
|
||||
.popup-internal-box {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.popup-internal-box,
|
||||
.menupopup-arrowscrollbox {
|
||||
padding: var(--uc-contextmenu-padding) !important;
|
||||
border-radius: var(--uc-contextmenu-border-radius) !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
menupopup menu[disabled="true"],
|
||||
menupopup menuitem[disabled="true"] {
|
||||
color: var(--panel-disabled-color) !important;
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
menupopup menu:hover:not([disabled=true]),
|
||||
menupopup menuitem:hover:not([disabled=true]),
|
||||
menupopup menu[_moz-menuactive]:not([disabled=true]),
|
||||
menupopup menuitem[_moz-menuactive]:not([disabled=true]) {
|
||||
background-color: var(--panel-item-hover-bgcolor) !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
menupopup menuitem:hover:active:not([disabled=true]),
|
||||
menupopup menu:hover:active:not([disabled=true]) {
|
||||
background-color: var(--panel-item-active-bgcolor) !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
menupopup menuseparator,
|
||||
panel menuseparator,
|
||||
#PopupSearchAutoComplete .searchbar-separator {
|
||||
margin: var(--uc-contextmenu-separator-vertical) 0 !important;
|
||||
padding: 0 !important;
|
||||
background: none !important;
|
||||
border-top: 1px solid var(--panel-separator-color) !important;
|
||||
|
||||
&::before {
|
||||
content: unset !important;
|
||||
}
|
||||
}
|
||||
|
||||
#PlacesToolbar menupopup[placespopup="true"] menuseparator::before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
menupopup menu,
|
||||
menupopup menuitem,
|
||||
menupopup menucaption,
|
||||
#PopupAutoComplete > richlistbox > richlistitem,
|
||||
#PopupSearchAutoComplete .autocomplete-richlistitem {
|
||||
padding: var(--uc-contextmenu-menuitem-padding-block) var(--uc-contextmenu-menuitem-padding-inline) !important;
|
||||
margin: var(--uc-contextmenu-menuitem-margin) !important;
|
||||
border-radius: var(--uc-contextmenu-menuitem-border-radius) !important;
|
||||
border: var(--uc-contextmenu-menuitem-border-width) solid transparent !important;
|
||||
background-clip: padding-box !important;
|
||||
min-height: calc(16px + (var(--uc-contextmenu-menuitem-padding-block) + var(--uc-contextmenu-menuitem-border-width)) * 2) !important;
|
||||
}
|
||||
|
||||
:not(:not(menubar)>menu, #ContentSelectDropdown) > menupopup > menuitem:not(.menuitem-iconic, [type="checkbox"], [type="radio"], .in-menulist, .in-menulist menuitem, .unified-nav-current),
|
||||
:not(:not(menubar)>menu, #ContentSelectDropdown) > menupopup > menu:not(.menu-iconic, [type="checkbox"], [type="radio"], .in-menulist, .in-menulist menu, .unified-nav-current),
|
||||
:not(:not(menubar)>menu, #ContentSelectDropdown) > menupopup > menucaption {
|
||||
padding-inline-start: calc(var(--uc-contextmenu-menuitem-padding-inline) + 16px + var(--uc-contextmenu-menuicon-margin-inline)) !important;
|
||||
}
|
||||
|
||||
.menu-iconic-icon {
|
||||
margin-block: 0 !important;
|
||||
margin-inline-end: 0 !important;
|
||||
margin-inline-start: 0 !important;
|
||||
}
|
||||
|
||||
menu > .menu-iconic-text,
|
||||
menuitem > .menu-iconic-text,
|
||||
/* Used for vertical context navigation tweak. */
|
||||
#context-navigation > .menuitem-iconic::after {
|
||||
padding-inline-start: var(--uc-contextmenu-menuicon-margin-inline) !important;
|
||||
}
|
||||
|
||||
.menu-text,
|
||||
.menu-iconic-text {
|
||||
margin-inline-start: 0 !important;
|
||||
padding-block: 0 !important;
|
||||
}
|
||||
|
||||
.menu-accel,
|
||||
.menu-iconic-accel {
|
||||
margin-inline-end: 0 !important;
|
||||
padding-block: 0 !important;
|
||||
opacity: 0.7 !important;
|
||||
}
|
||||
|
||||
.menu-iconic > .menu-iconic-left,
|
||||
.menuitem-iconic > .menu-iconic-left {
|
||||
padding-inline-end: 0 !important;
|
||||
padding-block: 0 !important;
|
||||
}
|
||||
|
||||
menuitem:is(.menuitem-iconic:not([data-usercontextid]), [type="checkbox"], [type="radio"], [checked], [selected]) .menu-iconic-icon {
|
||||
-moz-context-properties: fill !important;
|
||||
fill: currentColor !important;
|
||||
}
|
||||
|
||||
.menu-right,
|
||||
.menu-accel-container {
|
||||
appearance: none !important;
|
||||
margin-inline-end: 0 !important;
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor !important;
|
||||
}
|
||||
|
||||
menupopup menuitem:is([type="checkbox"], [type="radio"]) .menu-iconic-icon,
|
||||
menulist > menupopup > menuitem .menu-iconic-icon,
|
||||
menu .menu-right {
|
||||
height: 16px !important;
|
||||
width: 16px !important;
|
||||
}
|
||||
|
||||
menulist > menupopup > menuitem:is([checked="true"], [selected="true"])::before,
|
||||
menupopup > menuitem::before,
|
||||
menupopup > menu::before,
|
||||
menupopup > menuitem::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
menuitem:is([type="checkbox"], [checked="true"], [type="radio"]) > .menu-iconic-left > .menu-iconic-icon {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
menuitem[checked="true"] > .menu-iconic-left,
|
||||
menu > .menu-iconic-left,
|
||||
menuitem > .menu-iconic-left {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important
|
||||
}
|
||||
|
||||
#context-navigation:not([hidden]) {
|
||||
padding: 0 !important;
|
||||
--menuitem-min-width: calc(16px + var(--uc-contextmenu-menuitem-padding-inline) * 2) !important;
|
||||
min-width: calc(4 * var(--menuitem-min-width));
|
||||
}
|
||||
|
||||
#context-navigation > .menuitem-iconic > .menu-iconic-left > .menu-iconic-icon {
|
||||
width: 16px !important;
|
||||
height: 16px !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
#context-navigation {
|
||||
margin-inline: var(--uc-contextmenu-menuitem-margin-inline) !important;
|
||||
}
|
||||
|
||||
#context-navigation > .menuitem-iconic {
|
||||
justify-content: center !important;
|
||||
}
|
||||
|
||||
#context-navigation > .menuitem-iconic {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
#context-navigation > .menuitem-iconic {
|
||||
flex-grow: 1 !important;
|
||||
width: unset !important;
|
||||
}
|
||||
|
||||
#context-navigation > .menuitem-iconic[_moz-menuactive="true"] > .menu-iconic-left > .menu-iconic-icon {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
#context-bookmarkpage[starred] .menu-iconic-icon {
|
||||
fill: var(--lwt-toolbarbutton-icon-fill-attention, var(--toolbarbutton-icon-fill-attention, currentColor)) !important;
|
||||
}
|
||||
|
||||
@media (not (-moz-bool-pref: "uc.tweak.smaller-context-menu-text")) and (-moz-platform: windows) {
|
||||
.menu-text,
|
||||
.menu-iconic-text,
|
||||
panel .toolbarbutton-text,
|
||||
#PopupSearchAutoComplete .ac-title,
|
||||
/* Used for vertical context navigation tweak. */
|
||||
#context-navigation > .menuitem-iconic::after {
|
||||
margin-inline-start: 0 !important;
|
||||
margin-block: -3px -1px !important;
|
||||
padding-block: 0 !important;
|
||||
}
|
||||
|
||||
.menu-text,
|
||||
.menu-iconic-text,
|
||||
panel .toolbarbutton-text,
|
||||
#appMenu-fxa-status2,
|
||||
.panel-header,
|
||||
#PopupSearchAutoComplete,
|
||||
/* Used for vertical context navigation tweak. */
|
||||
#context-navigation > .menuitem-iconic::after {
|
||||
font-size: 13.6px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Dialog box
|
||||
*/
|
||||
|
||||
/* Dialog shadow and outline adjustments. */
|
||||
.dialogBox:not(.spotlightBox) {
|
||||
border-radius: 8px !important;
|
||||
outline: 0.5px solid var(--panel-separator-color) !important;
|
||||
box-shadow:
|
||||
0 20px 38px rgb(0 0 0 / 0.23),
|
||||
0 5px 9px rgb(0 0 0 / 0.22) !important;
|
||||
}
|
||||
|
||||
dialog {
|
||||
/* Dialog title. */
|
||||
& #titleContainer {
|
||||
/* Add larger gap between icon and title text. */
|
||||
& .titleIcon {
|
||||
padding-inline-end: 8px !important;
|
||||
}
|
||||
|
||||
/* Match font style in Edge. */
|
||||
& #titleText {
|
||||
font-size: 1.18em !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
/* Adjust vertical alignment of title text. */
|
||||
@media (-moz-platform: macOS) {
|
||||
&:not([noicon]) > #titleCropper {
|
||||
translate: 0 calc(-1px - max(.55 * var(--icon-size) - .6em, 0px)) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Input focus style. */
|
||||
& input:where([type="email"], [type="tel"], [type="text"], [type="password"], [type="url"], [type="number"]):focus {
|
||||
outline: 2px solid var(--in-content-box-border-color) !important;
|
||||
outline-offset: -2px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* FF123+ tab preview pop-up.
|
||||
* CSS reference: https://searchfox.org/mozilla-central/source/browser/components/tabpreview/tabpreview.css
|
||||
* Requires `browser.tabs.cardPreview.enabled` set to `true` */
|
||||
.tab-preview-container {
|
||||
background-color: var(--arrowpanel-background) !important;
|
||||
color: var(--arrowpanel-color) !important;
|
||||
border: 0.5px solid var(--panel-separator-color) !important;
|
||||
border-radius: 8.5px !important;
|
||||
|
||||
/* Place thumbnail at the top of the tooltip. */
|
||||
display: flex !important;
|
||||
flex-direction: column-reverse !important;
|
||||
|
||||
& .tab-preview-uri {
|
||||
color: var(--panel-description-color) !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/* Edge-Frfox - tree.css */
|
||||
|
||||
/* Change height of tree items and add rounded corners */
|
||||
treechildren::-moz-tree-row,
|
||||
treecol:not([hideheader="true"]),
|
||||
.tree-columnpicker-button {
|
||||
min-height: max(28px, 1.3em) !important;
|
||||
border-radius: 2px !important;
|
||||
}
|
||||
|
||||
/* Change the background colour on hover */
|
||||
@media not (prefers-contrast) {
|
||||
treechildren::-moz-tree-row(hover) {
|
||||
background-color: var(--toolbarbutton-hover-background) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Change the background colour when pressed */
|
||||
treechildren::-moz-tree-row(selected) {
|
||||
background-color: var(--toolbarbutton-active-background) !important;
|
||||
}
|
||||
|
||||
/* Change the background colour when focused */
|
||||
treechildren::-moz-tree-row(selected, focus) {
|
||||
background-color: var(--button-primary-bgcolor) !important;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-row(current, focus) {
|
||||
outline: var(--default-focusring);
|
||||
outline-color: var(--button-primary-bgcolor) !important;
|
||||
outline-offset: calc(-1 * var(--default-focusring-width));
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(selected),
|
||||
treechildren::-moz-tree-twisty(selected),
|
||||
treechildren::-moz-tree-cell-text(selected) {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(selected, focus),
|
||||
treechildren::-moz-tree-twisty(selected, focus),
|
||||
treechildren::-moz-tree-cell-text(selected, focus) {
|
||||
color: var(--button-primary-color) !important;
|
||||
}
|
||||
|
||||
/* Spacing between icon and label */
|
||||
treechildren::-moz-tree-image {
|
||||
margin-inline-end: 8px !important;
|
||||
}
|
||||
|
||||
/* Separator appearance */
|
||||
treechildren::-moz-tree-separator {
|
||||
border-top: 1px solid var(--toolbarseparator-color) !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
/* Change the colour of the drop feedback elements */
|
||||
treechildren::-moz-tree-cell-text(primary, dropOn) {
|
||||
background-color: var(--button-primary-bgcolor) !important;
|
||||
color: var(--button-primary-color) !important;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-drop-feedback {
|
||||
background-color: var(--toolbarbutton-icon-fill-attention) !important;
|
||||
border-radius: 2px !important;
|
||||
}
|
||||
|
||||
/* Change the appearance for the expandable items */
|
||||
treechildren::-moz-tree-twisty {
|
||||
padding-top: 0 !important;
|
||||
padding-inline: 4px !important;
|
||||
width: 16px !important;
|
||||
list-style-image: url("../icons/arrow-filled-right.svg") !important;
|
||||
}
|
||||
|
||||
treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed) {
|
||||
list-style-image: url("../icons/arrow-filled-left.svg") !important;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-twisty(open) {
|
||||
list-style-image: url("../icons/arrow-filled-down.svg") !important;
|
||||
}
|
||||
|
||||
/* Change the identation of child items */
|
||||
treechildren::-moz-tree-indentation {
|
||||
width: 16px !important;
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
/* Edge-Frfox - tweaks.css */
|
||||
|
||||
/*
|
||||
MARK: Disable drag space
|
||||
*/
|
||||
@media (-moz-bool-pref: "uc.tweak.disable-drag-space") {
|
||||
/* disable drag space above tabs */
|
||||
:root {
|
||||
--uc-tab-top-margin: 0px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Force tab colour
|
||||
*/
|
||||
/* force tabs to toolbar bg (useful for proton themes) (might experience some
|
||||
bugs with certain themes eg. dark text on dark background.) */
|
||||
@media (-moz-bool-pref: "uc.tweak.force-tab-colour") {
|
||||
:root[lwtheme] {
|
||||
--tab-selected-bgcolor: var(--toolbar-bgcolor) !important;
|
||||
--tab-selected-textcolor: var(--toolbar-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Remove tab separators
|
||||
*/
|
||||
@media (-moz-bool-pref: "uc.tweak.remove-tab-separators") {
|
||||
.tabbrowser-tab .tab-stack {
|
||||
&::before,
|
||||
&::after {
|
||||
content: initial !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Floating tabs
|
||||
*/
|
||||
@media (-moz-bool-pref: "uc.tweak.floating-tabs") {
|
||||
:root:not([lwtheme]),
|
||||
/* `--newtab-background-color-secondary used` to differentiate between a dark
|
||||
* private window and private window with the 'Dark' theme enabled.
|
||||
* May not be the most reliable method, but works for now up to FF 123. */
|
||||
:root[privatebrowsingmode="temporary"]:where([style*="--lwt-accent-color: rgb(28, 27, 34)"]:not([style*="--newtab-background-color-secondary: rgb(66, 65, 77)"])) {
|
||||
--lwt-accent-color: light-dark(#f3f3f3, #202020) !important;
|
||||
--lwt-accent-color-inactive: unset !important;
|
||||
--toolbar-bgcolor: light-dark(#fff, #3F3F3F) !important;
|
||||
--lwt-tab-text: light-dark(#262626, #fff) !important;
|
||||
--chrome-content-separator-color: light-dark(#bfbfbf, #535353) !important;
|
||||
--lwt-tab-line-color: light-dark(transparent, rgb(255 255 255 / 0.06)) !important;
|
||||
}
|
||||
|
||||
/* Uses :root body to avoid affecting other variables by changing
|
||||
* --toolbar-color. */
|
||||
:root body {
|
||||
/* Use tabbar colours for better readability when using custom themes. */
|
||||
--toolbar-color: var(--lwt-text-color) !important;
|
||||
--toolbarbutton-icon-fill: var(--lwt-text-color) !important;
|
||||
|
||||
&:-moz-window-inactive {
|
||||
--toolbar-color: var(--lwt-text-color-inactive, var(--lwt-text-color)) !important;
|
||||
--toolbarbutton-icon-fill: var(--lwt-text-color-inactive, var(--lwt-text-color)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (-moz-gtk-csd-available) {
|
||||
:root:not([lwtheme]) {
|
||||
--toolbar-bgcolor: color-mix(in srgb, -moz-dialog 80%, #fff) !important;
|
||||
--toolbar-field-background-color: Field !important;
|
||||
--lwt-tab-text: -moz-dialogtext !important;
|
||||
--lwt-tab-line-color: rgb(255 255 255 / 0.06) !important;
|
||||
--lwt-accent-color: var(--toolbox-non-lwt-bgcolor) !important;
|
||||
|
||||
&:-moz-window-inactive {
|
||||
--lwt-accent-color: var(--toolbox-non-lwt-bgcolor-inactive) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Shadow for navbar and tabs */
|
||||
:root,
|
||||
:root .tabbrowser-tab {
|
||||
--uc-titlebar-shadow: none;
|
||||
--uc-tab-shadow-outline: rgb(0 0 0 / .11);
|
||||
--uc-tab-shadow: 0 2.5px 3px 1px rgb(0 0 0 / .08);
|
||||
|
||||
& [brighttext],
|
||||
& [brighttext] .tabbrowser-tab {
|
||||
--uc-titlebar-shadow: none;
|
||||
--uc-tab-shadow-outline: transparent;
|
||||
--uc-tab-shadow: 0 2px 3px rgb(0 0 0 / .12);
|
||||
}
|
||||
|
||||
& #TabsToolbar {
|
||||
--tab-border-radius: 8px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove toolbar bg */
|
||||
#nav-bar,
|
||||
#PersonalToolbar {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
/* invert bottom corner radius for tabs */
|
||||
.tab-background {
|
||||
border-radius: var(--tab-border-radius) !important;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-background[selected] {
|
||||
border: 1px solid var(--lwt-tab-line-color, var(--lwt-tabs-border-color, transparent)) !important;
|
||||
outline: 1px solid var(--uc-tab-shadow-outline) !important;
|
||||
|
||||
/* Fractional scaling adjustments (150%, 175%, etc.) */
|
||||
@media (1dppx < resolution < 2dppx) {
|
||||
outline-width: 1.5px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* adjust spacing of area above tabs */
|
||||
@media not (-moz-platform: macos) {
|
||||
:root[sizemode="maximized"] {
|
||||
--uc-tab-top-margin: 4px !important;
|
||||
}
|
||||
}
|
||||
|
||||
:root:is([inFullscreen], :not([tabsintitlebar])) {
|
||||
--uc-tab-top-margin: 4px !important;
|
||||
}
|
||||
|
||||
@media (-moz-bool-pref: "uc.tweak.disable-drag-space") {
|
||||
:root {
|
||||
--uc-tab-top-margin: 4px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* IMPORTANT: Adds padding and removes margin on top of tabs to allow user to
|
||||
* select it from the top edge of the window */
|
||||
#tabbrowser-tabs {
|
||||
margin-top: calc(var(--uc-tab-top-margin) - 4px) !important;
|
||||
|
||||
& .tabbrowser-tab,
|
||||
& #tabs-newtab-button {
|
||||
padding-top: 4px !important;
|
||||
}
|
||||
}
|
||||
|
||||
#scrollbutton-up:not(.menupopup-scrollbutton),
|
||||
#scrollbutton-down:not(.menupopup-scrollbutton) {
|
||||
&,
|
||||
& ~ spacer {
|
||||
margin-top: 4px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* move tab shadow behind urlbar */
|
||||
.tabbrowser-tab[visuallyselected="true"],
|
||||
#nav-bar {
|
||||
z-index: 2 !important;
|
||||
}
|
||||
|
||||
/* tab shadow adjustments */
|
||||
.tabbrowser-tab {
|
||||
overflow-clip-margin: 8px !important;
|
||||
}
|
||||
|
||||
#tabbrowser-arrowscrollbox > *,
|
||||
#scrollbutton-up:not(.menupopup-scrollbutton),
|
||||
#scrollbutton-up:not(.menupopup-scrollbutton) ~ spacer,
|
||||
#scrollbutton-down:not(.menupopup-scrollbutton) {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
#tabbrowser-arrowscrollbox {
|
||||
margin-bottom: -8px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Vertical context nav
|
||||
*/
|
||||
@media (not (-moz-platform: macos)) and (not (-moz-bool-pref: "uc.tweak.revert-context-menu")) {
|
||||
@media (-moz-bool-pref: "uc.tweak.vertical-context-navigation") {
|
||||
#context-navigation {
|
||||
/* Stack menu items vertically. */
|
||||
flex-direction: column !important;
|
||||
|
||||
/* Align the image and label to the left edge. */
|
||||
& > .menuitem-iconic {
|
||||
justify-content: start !important;
|
||||
|
||||
&::after {
|
||||
/* Get the menuitem label from the aria-label attribute. */
|
||||
content: attr(aria-label);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Hide access key
|
||||
*/
|
||||
@media (-moz-bool-pref: "uc.tweak.context-menu.hide-access-key") {
|
||||
:where(menuitem, menu) > :is(.menu-text, .menu-iconic-text)[value]:not([value=""]) {
|
||||
&::before {
|
||||
/* The default value is -moz-label-content, which takes the data from the
|
||||
* value attribute and highlights the character found in the accesskey
|
||||
* attribute. This skips that and uses the value attribute directly. */
|
||||
content: attr(value) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Show tab close button
|
||||
*/
|
||||
@media (-moz-bool-pref: "uc.tweak.show-tab-close-button-on-hover") {
|
||||
#tabbrowser-tabs[closebuttons="activetab"] .tabbrowser-tab:not([pinned], [selected]):hover .tab-close-button {
|
||||
display: flex !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Hide forward button
|
||||
*/
|
||||
@media (-moz-bool-pref: "uc.tweak.hide-forward-button") {
|
||||
:root:not([customizing]) #forward-button[disabled] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MARK: Hide Firefox account
|
||||
*/
|
||||
@media (-moz-bool-pref: "uc.tweak.context-menu.hide-firefox-account") {
|
||||
#appMenu-fxa-status2,
|
||||
#appMenu-fxa-separator {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M8 0a2.5 2.5 0 0 0-2.396 3.213l-2.24-.847a1.746 1.746 0 0 0-1.245 3.26L5 6.736v2.398l-1.643 4.524a1.747 1.747 0 0 0 3.284 1.194L8 11.145l1.365 3.702a1.745 1.745 0 0 0 3.278-1.2L11 9.124V6.73l2.867-1.087a1.752 1.752 0 1 0-1.244-3.278l-2.227.843A2.5 2.5 0 0 0 8 0zm0 1a1.5 1.5 0 1 1 0 2.999 1.5 1.5 0 0 1 0-3zM2.729 3.252a.74.74 0 0 1 .28.05l4.104 1.551c.571.216 1.201.216 1.772 0l4.094-1.552a.754.754 0 1 1 .535 1.41l-3.192 1.208a.498.498 0 0 0-.322.467V9.21c0 .058.01.117.03.171l1.673 4.608a.745.745 0 0 1-1.4.511l-1.37-3.719c-.319-.867-1.547-.866-1.865.002L5.703 14.51a.747.747 0 1 1-1.404-.511l1.67-4.606A.497.497 0 0 0 6 9.222v-2.83a.5.5 0 0 0-.32-.467L2.479 4.693a.745.745 0 0 1 .25-1.44z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 834 B |
@@ -0,0 +1,13 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="a" x1="4.22" x2="11.78" y1="7.979" y2="7.979" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#b2b2b2" offset="0"/>
|
||||
<stop stop-color="#b2b2b2" stop-opacity="0" offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="#303030" d="M0 0h16v16H0z"/>
|
||||
<g fill="url(#a)">
|
||||
<circle cx="8" cy="5.767" r="2.433"/>
|
||||
<path d="M5.244 8.867c-.613 0-1.024.46-1.024 1.107 0 1.949 1.996 2.651 3.78 2.651 1.784 0 3.78-.702 3.78-2.651 0-.648-.41-1.107-1.023-1.107z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 584 B |
@@ -0,0 +1,7 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#fafafa" d="M0 0h16v16H0z"/>
|
||||
<g fill="#8f8f8f">
|
||||
<circle cx="8" cy="5.767" r="2.433"/>
|
||||
<path d="M5.244 8.867c-.613 0-1.024.46-1.024 1.107 0 1.949 1.996 2.651 3.78 2.651 1.784 0 3.78-.702 3.78-2.651 0-.648-.41-1.107-1.023-1.107z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 332 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M4 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h4.35a5.769 5.769 0 0 1-1.076-1H4.001a1 1 0 0 1-1-1h3.605a5.376 5.376 0 0 1-.406-1H3V2a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v4.199a5.2 5.2 0 0 1 1 .398V2a2 2 0 0 0-2-2H4zm1 2a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H5zm0 1h6v1H5V3z"/>
|
||||
<path d="M16 11.5a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0zm-4-2a.5.5 0 0 0-1 0V11H9.5a.5.5 0 0 0 0 1H11v1.5a.5.5 0 0 0 1 0V12h1.5a.5.5 0 0 0 0-1H12z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 571 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M8 11.5a.547.547 0 0 1-.389-.162L2.146 5.854a.5.5 0 1 1 .707-.705L8 10.313l5.146-5.164a.5.5 0 1 1 .71.705l-5.466 5.484A.55.55 0 0 1 8 11.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 281 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path transform="rotate(90)" transform-origin="center" d="M6.479 2.729 11.1 7.35a.92.92 0 0 1 0 1.3l-4.621 4.621a.72.72 0 0 1-1.229-.509V3.237a.72.72 0 0 1 1.229-.508z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 300 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path transform="rotate(180)" transform-origin="center" d="M6.479 2.729 11.1 7.35a.92.92 0 0 1 0 1.3l-4.621 4.621a.72.72 0 0 1-1.229-.509V3.237a.72.72 0 0 1 1.229-.508z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 301 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M6.479 2.729 11.1 7.35a.92.92 0 0 1 0 1.3l-4.621 4.621a.72.72 0 0 1-1.229-.509V3.237a.72.72 0 0 1 1.229-.508z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 251 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path transform="scale(-1 1) translate(-16 0)" d="M5.5 2a.5.5 0 0 0-.354.853L10.313 8l-5.167 5.146a.5.5 0 0 0 .707.71l5.484-5.468a.548.548 0 0 0 0-.777L5.853 2.143A.496.496 0 0 0 5.5 2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 318 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M5.5 2a.5.5 0 0 0-.354.853L10.313 8l-5.167 5.146a.5.5 0 0 0 .707.71l5.484-5.468a.548.548 0 0 0 0-.777L5.853 2.143A.496.496 0 0 0 5.5 2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 277 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M8 4.5a.547.547 0 0 0-.389.162l-5.465 5.484a.5.5 0 1 0 .707.705L8 5.687l5.146 5.164a.5.5 0 1 0 .71-.705L8.388 4.662A.55.55 0 0 0 8 4.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 277 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M14.13 7c-.478 0-.957.18-1.322.547l-4.828 4.83a2.2 2.2 0 0 0-.578 1.02l-.375 1.498a.889.889 0 0 0 1.078 1.078l1.498-.375a2.2 2.2 0 0 0 1.02-.578l4.828-4.828A1.872 1.872 0 0 0 14.13 7zm-.002 1.002c.427.002.671.224.8.537.13.314.116.643-.187.947l-4.826 4.826a1.204 1.204 0 0 1-.556.315l-1.316.332.33-1.32H8.37c.053-.21.163-.403.316-.557l4.828-4.83a.85.85 0 0 1 .614-.25zm-6.266 7 .002.002H7.86z"/>
|
||||
<path d="M12.94.182c-.08.004-.162.02-.242.045l-7 2.188a.998.998 0 0 0-.701.953V11.5a2.5 2.5 0 1 0 1 2V6.368l7-2.188v2.055c.32-.137.66-.213 1-.229V1.18c0-.59-.5-1.032-1.057-.998zm.057.998v1.953l-7 2.188V3.368l7-2.188zM3.497 12a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 801 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<defs><clipPath id="a"><path d="M2.1 0 16 13.9V0zM0 .7V16h15.3z"/></clipPath></defs>
|
||||
<path clip-path="url(#a)" d="M12.94.182c-.08.004-.162.017-.242.043l-7 2.188a1 1 0 0 0-.701.955V11.5a2.5 2.5 0 1 0 1 2V6.368l7-2.188V9.5a2.5 2.5 0 1 0 1 2V1.18c0-.59-.5-1.032-1.057-.998zm.057.998v1.953l-7 2.188V3.368l7-2.188zm-1.5 8.82a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3zm-8 2a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3z"/>
|
||||
<path d="M.5 0a.5.5 0 0 0-.354.146.5.5 0 0 0 0 .707l15 15a.5.5 0 0 0 .707 0 .5.5 0 0 0 0-.707l-15-15A.5.5 0 0 0 .499 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 658 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M12.94.182c-.08.004-.162.017-.242.043l-7 2.188a1 1 0 0 0-.701.955V11.5a2.5 2.5 0 1 0 1 2V6.368l7-2.188V9.5a2.5 2.5 0 1 0 1 2V1.18c0-.59-.5-1.032-1.057-.998zm.057.998v1.953l-7 2.188V3.368l7-2.188zm-1.5 8.82a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3zm-8 2a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 426 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M7.527.5a.5.5 0 0 0-.361.127L.322 6.764a.998.998 0 0 0 0 1.473l6.844 6.137a.5.5 0 0 0 .707-.04.5.5 0 0 0-.04-.706L1.558 8h13.44a.5.5 0 0 0 0-1H1.557l6.277-5.627a.5.5 0 0 0 .039-.707A.5.5 0 0 0 7.527.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 343 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M8.002.3a.99.99 0 0 0-.898.557L5.174 4.77l-4.316.627a1 1 0 0 0-.555 1.705l3.123 3.045-.736 4.3A1 1 0 0 0 4.14 15.5l3.862-2.029 3.859 2.03a1 1 0 0 0 1.45-1.056l-.736-4.299 3.123-3.045a1 1 0 0 0-.554-1.705l-4.316-.627L8.898.857A.987.987 0 0 0 8 .301zm0 1 1.93 3.91c.146.296.426.5.752.548l4.318.629-3.125 3.043c-.236.23-.343.562-.287.886l.738 4.3-3.86-2.03a1.002 1.002 0 0 0-.932 0l-3.86 2.03.737-4.3a1.003 1.003 0 0 0-.287-.886L1.003 6.387l4.316-.63a.998.998 0 0 0 .752-.546z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 615 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="context-fill" stroke-opacity="context-fill-opacity" stroke-linecap="round" stroke-width="1.001">
|
||||
<path d="m8 12.95-4.09 2.151a.501.501 0 0 1-.727-.528l.731-4.266a.561.562 0 0 0-.161-.498L.655 6.79a.501.501 0 0 1 .278-.855l4.28-.623a.561.562 0 0 0 .423-.307L7.55 1.123a.501.501 0 0 1 .9 0l2.031 4.115a.475.475 0 0 0 .426.264H15.5"/>
|
||||
<path d="M15.5 8.5h-5M10.5 11.5h5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 463 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M8.002.3a.99.99 0 0 0-.898.557L5.174 4.77l-4.316.627a1 1 0 0 0-.555 1.705l3.123 3.045-.736 4.3A1 1 0 0 0 4.14 15.5l3.862-2.029 3.859 2.03a1 1 0 0 0 1.45-1.056l-.736-4.299 3.123-3.045a1 1 0 0 0-.554-1.705l-4.316-.627L8.898.857A.987.987 0 0 0 8 .301z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 390 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<defs><clipPath id="a"><path d="M2.1 0 16 13.9V0zM0 .7V16h15.3z"/></clipPath></defs>
|
||||
<path clip-path="url(#a)" d="M6.119 0a1.5 1.5 0 0 0-1.34.826L4.189 2H2.5A2.5 2.5 0 0 0 0 4.5v8A2.5 2.5 0 0 0 2.5 15h11a2.5 2.5 0 0 0 2.5-2.5v-8A2.5 2.5 0 0 0 13.5 2h-1.689L11.225.83A1.501 1.501 0 0 0 9.883 0H6.12zm0 1h3.764a.5.5 0 0 1 .447.277l.725 1.445c.085.17.256.278.445.278h2A1.5 1.5 0 0 1 15 4.5v8a1.5 1.5 0 0 1-1.5 1.5h-11A1.5 1.5 0 0 1 1 12.5v-8A1.5 1.5 0 0 1 2.5 3h1.998a.5.5 0 0 0 .447-.276l.727-1.449A.5.5 0 0 1 6.12 1zm1.877 3a4 4 0 1 0 0 8 4 4 0 0 0 0-8zm0 1a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"/>
|
||||
<path d="M.5 0a.5.5 0 0 0-.354.146.5.5 0 0 0 0 .707l15 15a.5.5 0 0 0 .707 0 .5.5 0 0 0 0-.707l-15-15A.5.5 0 0 0 .499 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 845 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M6.119 0a1.5 1.5 0 0 0-1.34.826L4.189 2H2.5A2.5 2.5 0 0 0 0 4.5v8A2.5 2.5 0 0 0 2.5 15h11a2.5 2.5 0 0 0 2.5-2.5v-8A2.5 2.5 0 0 0 13.5 2h-1.689L11.225.83A1.501 1.501 0 0 0 9.883 0H6.12zm0 1h3.764a.5.5 0 0 1 .447.277l.725 1.445c.085.17.256.278.445.278h2A1.5 1.5 0 0 1 15 4.5v8a1.5 1.5 0 0 1-1.5 1.5h-11A1.5 1.5 0 0 1 1 12.5v-8A1.5 1.5 0 0 1 2.5 3h1.998a.5.5 0 0 0 .447-.276l.727-1.449A.5.5 0 0 1 6.12 1zm1.877 3a4 4 0 1 0 0 8 4 4 0 0 0 0-8zm0 1a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 613 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<defs><clipPath id="a"><path d="M2.1 0 16 13.9V0zM0 .7V16h15.3z"/></clipPath></defs>
|
||||
<path clip-path="url(#a)" d="M4 1a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h8a3 3 0 0 0 3-3V4a3 3 0 0 0-3-3H4zm0 1h8a2 2 0 0 1 2 2v8c0 .373-.102.72-.28 1.02L9.05 8.432a1.5 1.5 0 0 0-2.101 0l-4.67 4.588a1.988 1.988 0 0 1-.28-1.02V4a2 2 0 0 1 2-2zm6.5 2a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zm0 1a.5.5 0 1 1 0 1 .5.5 0 0 1 0-1zM8 9.002c.127 0 .252.047.35.143l4.662 4.582A1.99 1.99 0 0 1 12 14H4c-.37 0-.715-.099-1.012-.273L7.65 9.145A.496.496 0 0 1 8 9.002z"/>
|
||||
<path d="M.5 0a.5.5 0 0 0-.354.146.5.5 0 0 0 0 .707l15 15a.5.5 0 0 0 .707 0 .5.5 0 0 0 0-.707l-15-15A.5.5 0 0 0 .499 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 786 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M4 1a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h8a3 3 0 0 0 3-3V4a3 3 0 0 0-3-3H4zm0 1h8a2 2 0 0 1 2 2v8c0 .373-.102.72-.28 1.02L9.05 8.432a1.5 1.5 0 0 0-2.101 0l-4.67 4.588a1.988 1.988 0 0 1-.28-1.02V4a2 2 0 0 1 2-2zm6.5 2a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3zm0 1a.5.5 0 1 1 0 1 .5.5 0 0 1 0-1zM8 9.002c.127 0 .252.047.35.143l4.662 4.582A1.99 1.99 0 0 1 12 14H4c-.37 0-.715-.099-1.012-.273L7.65 9.145A.496.496 0 0 1 8 9.002z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 554 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M14.5 2a.5.5 0 0 0-.379.174l-9.129 10.57-4.12-4.578a.5.5 0 0 0-.706-.037.5.5 0 0 0-.037.705l4.5 5a.5.5 0 0 0 .75-.008l9.5-11a.5.5 0 0 0-.053-.705A.5.5 0 0 0 14.5 2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 306 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M2.5 2a.5.5 0 0 0-.353.146.5.5 0 0 0 0 .708L7.293 8l-5.146 5.145a.5.5 0 0 0 0 .707.5.5 0 0 0 .707 0l5.5-5.5a.5.5 0 0 0 0-.707l-5.5-5.5A.5.5 0 0 0 2.5 2z"/>
|
||||
<path d="M8.146 2.146a.5.5 0 0 0 0 .707l5.146 5.146-5.146 5.146a.5.5 0 0 0 0 .707.5.5 0 0 0 .707 0l5.5-5.5a.5.5 0 0 0 0-.707l-5.5-5.5a.5.5 0 0 0-.707 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 452 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="9" height="9" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M1 .412a.588.588 0 0 0-.416.172.588.588 0 0 0 0 .832L3.668 4.5.584 7.584a.588.588 0 0 0 0 .832.588.588 0 0 0 .832 0L4.5 5.332l3.084 3.084a.588.588 0 0 0 .832 0 .588.588 0 0 0 0-.832L5.332 4.5l3.084-3.084a.588.588 0 0 0 0-.832.588.588 0 0 0-.832 0L4.5 3.668 1.416.584A.588.588 0 0 0 1 .412z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 429 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M2.5 2a.5.5 0 0 0-.353.146.5.5 0 0 0 0 .708L7.293 8l-5.146 5.145a.5.5 0 0 0 0 .707.5.5 0 0 0 .707 0L8 8.706l5.146 5.146a.5.5 0 0 0 .706 0 .5.5 0 0 0 0-.707L8.708 8l5.146-5.146a.5.5 0 0 0 0-.707.5.5 0 0 0-.707 0L8 7.292 2.854 2.146A.5.5 0 0 0 2.5 2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 390 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M2 0C.901 0 0 .901 0 2v1.5a.5.5 0 0 0 .5.5.5.5 0 0 0 .5-.5V2c0-.558.442-1 1-1h1.5A.5.5 0 0 0 4 .5a.5.5 0 0 0-.5-.5H2zm10.5 0a.5.5 0 0 0-.5.5.5.5 0 0 0 .5.5H14c.558 0 1 .442 1 1v1.5a.5.5 0 0 0 .5.5.5.5 0 0 0 .5-.5V2c0-1.099-.901-2-2-2h-1.5zm-8 2A2.508 2.508 0 0 0 2 4.5v7C2 12.875 3.125 14 4.5 14h7c1.375 0 2.5-1.125 2.5-2.5v-7C14 3.125 12.875 2 11.5 2h-7zm0 1H7v1.5C7 5.323 7.677 6 8.5 6H13v5.5c0 .834-.666 1.5-1.5 1.5h-7c-.834 0-1.5-.666-1.5-1.5v-7C3 3.666 3.666 3 4.5 3zM8 3h3.5c.834 0 1.5.666 1.5 1.5V5H8.5a.493.493 0 0 1-.5-.5V3zM.5 12a.5.5 0 0 0-.5.5V14c0 1.099.901 2 2 2h1.5a.5.5 0 0 0 .5-.5.5.5 0 0 0-.5-.5H2c-.558 0-1-.442-1-1v-1.5a.5.5 0 0 0-.5-.5zm15 0a.5.5 0 0 0-.5.5V14c0 .558-.442 1-1 1h-1.5a.5.5 0 0 0-.5.5.5.5 0 0 0 .5.5H14c1.099 0 2-.901 2-2v-1.5a.5.5 0 0 0-.5-.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 922 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M3.5 0a.5.5 0 0 0-.5.5v8.504a2 2 0 0 0 2 2h1.004v2.998a2 2 0 1 0 4 0v-2.998H11a2 2 0 0 0 2-2V.5a.5.5 0 0 0-.5-.5h-9zM4 1h4v1.5a.5.5 0 1 0 1 0V1h1v2.502a.5.5 0 0 0 1 0V1h1v6.004H4V1zm0 7.004h8v1a1 1 0 0 1-1 1H9.504a.5.5 0 0 0-.5.5v3.498a1 1 0 1 1-2 0v-3.498a.5.5 0 0 0-.5-.5H5a1 1 0 0 1-1-1v-1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 435 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<defs><clipPath id="a"><path d="M2.1 0 16 13.9V0zM0 .7V16h15.3z"/></clipPath></defs>
|
||||
<path clip-path="url(#a)" d="M8 0C4.692 0 2 2.692 2 6v3.4l-.928 2.229c-.265.636.225 1.371.914 1.371H5.5c0 1.375 1.125 2.5 2.5 2.5s2.5-1.125 2.5-2.5h3.514c.69 0 1.18-.735.914-1.371L14.001 9.4V6c0-3.308-2.692-6-6-6zm0 1c2.767 0 5 2.233 5 5v3.5a.5.5 0 0 0 .04.191l.96 2.31H2l.96-2.31A.5.5 0 0 0 3 9.5V6c0-2.767 2.233-5 5-5zM6.5 13h3c0 .834-.666 1.5-1.5 1.5s-1.5-.666-1.5-1.5z" style="-inkscape-stroke:none;color:#000;stroke-linecap:round;stroke-linejoin:round"/>
|
||||
<path d="M.5 0a.5.5 0 0 0-.354.146.5.5 0 0 0 0 .707l15 15a.5.5 0 0 0 .707 0 .5.5 0 0 0 0-.707l-15-15A.5.5 0 0 0 .499 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 801 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M8 0C4.692 0 2 2.692 2 6v3.4l-.928 2.229c-.265.636.225 1.371.914 1.371H5.5c0 1.375 1.125 2.5 2.5 2.5s2.5-1.125 2.5-2.5h3.514c.69 0 1.18-.735.914-1.371L14.001 9.4V6c0-3.308-2.692-6-6-6zm0 1c2.767 0 5 2.233 5 5v3.5a.5.5 0 0 0 .04.191l.96 2.31H2l.96-2.31A.5.5 0 0 0 3 9.5V6c0-2.767 2.233-5 5-5zM6.5 13h3c0 .834-.666 1.5-1.5 1.5s-1.5-.666-1.5-1.5z" style="-inkscape-stroke:none;color:#000;stroke-linecap:round;stroke-linejoin:round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 569 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M11.5 0a4.5 4.5 0 0 0-4.418 5.36L.656 12.016a2.358 2.358 0 0 0 3.375 3.294l6.365-6.447a4.5 4.5 0 0 0 5.207-6.215.503.503 0 0 0-.81-.149L12.5 4.793 11.207 3.5 13.5 1.207A.5.5 0 0 0 13.353.4 4.49 4.49 0 0 0 11.499 0zm0 1c.247 0 .487.026.719.074l-2.072 2.072a.5.5 0 0 0 0 .707l2 2a.5.5 0 0 0 .707 0l2.072-2.072a3.5 3.5 0 0 1-4.52 4.045.502.502 0 0 0-.514.123L3.32 14.61a1.358 1.358 0 0 1-1.943-1.896L7.99 5.859a.498.498 0 0 0 .123-.473A3.5 3.5 0 0 1 11.5.999z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 598 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M8 0a.5.5 0 0 0-.5.5v10.79L3.854 7.644a.5.5 0 1 0-.707.707l4.5 4.5a.5.5 0 0 0 .707 0l4.5-4.5a.5.5 0 0 0-.707-.707L8.501 11.29V.5a.5.5 0 0 0-.5-.5z"/>
|
||||
<path d="M1.5 12a.5.5 0 0 0-.5.5v1C1 14.875 2.125 16 3.5 16h9c1.375 0 2.5-1.125 2.5-2.5v-1a.5.5 0 0 0-1 0v1c0 .834-.666 1.5-1.5 1.5h-9c-.834 0-1.5-.666-1.5-1.5v-1a.5.5 0 0 0-.5-.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 474 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M5.5 0A2.5 2.5 0 0 0 3 2.5v8A2.5 2.5 0 0 0 5.5 13h8a2.5 2.5 0 0 0 2.5-2.5v-8A2.5 2.5 0 0 0 13.5 0h-8zm0 1H7v1.5A1.5 1.5 0 0 0 8.5 4H15v6.5a1.5 1.5 0 0 1-1.5 1.5h-8A1.5 1.5 0 0 1 4 10.5v-8A1.5 1.5 0 0 1 5.5 1zM8 1h5.5A1.5 1.5 0 0 1 15 2.5V3H8.5a.5.5 0 0 1-.5-.5V1zM2 3.05A2.5 2.5 0 0 0 0 5.5V12a4 4 0 0 0 4 4h6.5a2.5 2.5 0 0 0 2.45-2h-1.036a1.5 1.5 0 0 1-1.414 1H4a3 3 0 0 1-3-3V5.5a1.5 1.5 0 0 1 1-1.414V3.05z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 551 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M11.72 6.586c-.799.537-1.787 1.167-3.303 1.422a.5.5 0 0 0-.418.492v2.564c0 2.288 1.648 4.08 3.822 4.902a.5.5 0 0 0 .356 0c2.174-.822 3.822-2.614 3.822-4.902V8.5a.5.5 0 0 0-.419-.492c-1.514-.255-2.503-.885-3.302-1.422a.5.5 0 0 0-.56 0zm.28.992c.716.47 1.69.982 3 1.281v2.205c0 1.741-1.23 3.108-3 3.848-1.77-.74-3-2.107-3-3.848V8.86a9.083 9.083 0 0 0 3-1.28z"/>
|
||||
<path d="M6 0C4.901 0 4 .901 4 2v10c0 1.099.901 2 2 2h1.836a5.373 5.373 0 0 1-.488-1H6c-.558 0-1-.442-1-1V2c0-.558.442-1 1-1h6c.558 0 1 .442 1 1v3.865c.203.139.748.48 1 .592V2c0-1.1-.902-2-2-2H6z"/>
|
||||
<path d="M3 2.271C2.404 2.619 2 3.265 2 4v8.5C2 14.427 3.573 16 5.5 16h4.252a7.236 7.236 0 0 1-1.154-1H5.5A2.492 2.492 0 0 1 3 12.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 837 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M6 0a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H6zm0 1h6a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM3 2.268C2.402 2.614 2 3.26 2 4v8.5A3.5 3.5 0 0 0 5.5 16H10c.74 0 1.387-.402 1.732-1H5.5A2.5 2.5 0 0 1 3 12.5V2.27z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 397 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M3.508 0a.5.5 0 0 0-.428.771L7.404 7.43 5.48 10.39a3 3 0 1 0 .78.636L8 8.347l1.74 2.68a3 3 0 1 0 .78-.636L3.92.23a.503.503 0 0 0-.412-.228zm8.984 0a.503.503 0 0 0-.412.229L8.596 5.594l.598.918L12.92.772A.5.5 0 0 0 12.493 0zM4 11a2 2 0 1 1 0 4 2 2 0 0 1 0-4zm8 0a2 2 0 1 1 0 4 2 2 0 0 1 0-4z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 432 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M8 .5A2.5 2.5 0 0 0 5.5 3H1a.5.5 0 0 0-.5.5l.008.09A.5.5 0 0 0 1 4h.553L2.85 14.229A2 2 0 0 0 4.836 16h6.328a2 2 0 0 0 1.986-1.771L14.445 4H15a.5.5 0 0 0 0-1h-4.5A2.5 2.5 0 0 0 8 .5zm0 1A1.5 1.5 0 0 1 9.5 3h-3A1.5 1.5 0 0 1 8 1.5zM2.56 4h10.877l-1.28 10.116a.998.998 0 0 1-.993.884H4.836a.998.998 0 0 1-.992-.884zM6.5 6.5c-.276 0-.5.196-.5.438v5.124l.008.078c.042.204.247.36.492.36.276 0 .5-.196.5-.438V6.938l-.008-.079C6.95 6.655 6.745 6.5 6.5 6.5zm3 0c-.276 0-.5.196-.5.438v5.124l.008.078c.042.204.247.36.492.36.276 0 .5-.196.5-.438V6.938l-.008-.079C9.95 6.655 9.745 6.5 9.5 6.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 723 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M6.5 0a1.5 1.5 0 0 0-1.414 1H3.5A1.5 1.5 0 0 0 2 2.5v12A1.5 1.5 0 0 0 3.5 16h3a.5.5 0 0 0 0-1h-3a.5.5 0 0 1-.5-.5v-12a.5.5 0 0 1 .5-.5h1.586A1.5 1.5 0 0 0 6.5 3h3a1.5 1.5 0 0 0 1.414-1H12.5a.5.5 0 0 1 .5.5v1a.5.5 0 0 0 1 0v-1A1.5 1.5 0 0 0 12.5 1h-1.586A1.5 1.5 0 0 0 9.5 0h-3zm0 1h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1zm3 4A1.5 1.5 0 0 0 8 6.5v8A1.5 1.5 0 0 0 9.5 16h5a1.5 1.5 0 0 0 1.5-1.5v-8A1.5 1.5 0 0 0 14.5 5h-5zm0 1h5a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-.5.5h-5a.5.5 0 0 1-.5-.5v-8a.5.5 0 0 1 .5-.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 639 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path transform="scale(-1 1) translate(-16 0)" d="M1.5 0a.5.5 0 0 0-.5.5v4.9a.6.6 0 0 0 .6.6h4.9a.5.5 0 0 0 0-1H2.906L6.38 1.98a4 4 0 1 1 5.248 6.04l-8.172 7.103a.5.5 0 1 0 .656.754l8.172-7.104a5.001 5.001 0 0 0-6.56-7.547L2 4.464V.5a.5.5 0 0 0-.5-.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 384 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M6 0a2 2 0 0 0-2 2v.5a.5.5 0 0 0 1 0V2a1 1 0 0 1 1-1h.5a.5.5 0 0 0 0-1H6zm2.5 0a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-3zm5 0a.5.5 0 0 0 0 1h.5a1 1 0 0 1 1 1v.5a.5.5 0 0 0 1 0V2a2 2 0 0 0-2-2h-.5zM2 4a2 2 0 0 0-2 2v6.5A3.5 3.5 0 0 0 3.5 16H10a2 2 0 0 0 2-2v-1h-1v1a1 1 0 0 1-1 1H3.5A2.5 2.5 0 0 1 1 12.5V6a1 1 0 0 1 1-1h1V4H2zm2.5 0a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 1 0v-3a.5.5 0 0 0-.5-.5zm11 0a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 1 0v-3a.5.5 0 0 0-.5-.5zm-11 5a.5.5 0 0 0-.5.5v.5a2 2 0 0 0 2 2h.5a.5.5 0 0 0 0-1H6a1 1 0 0 1-1-1v-.5a.5.5 0 0 0-.5-.5zm11 0a.5.5 0 0 0-.5.5v.5a1 1 0 0 1-1 1h-.5a.5.5 0 0 0 0 1h.5a2 2 0 0 0 2-2v-.5a.5.5 0 0 0-.5-.5zm-7 2a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-3z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 813 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M1.5 0a.5.5 0 0 0-.5.5v4.9a.6.6 0 0 0 .6.6h4.9a.5.5 0 0 0 0-1H2.906L6.38 1.98a4 4 0 1 1 5.248 6.04l-8.172 7.103a.5.5 0 1 0 .656.754l8.172-7.104a5.001 5.001 0 0 0-6.56-7.547L2 4.464V.5a.5.5 0 0 0-.5-.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 343 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M11.25.815a2.783 2.783 0 0 1 4.066 3.796l-.13.14-9.606 9.605a2 2 0 0 1-.723.463l-.165.053-4.055 1.106a.5.5 0 0 1-.63-.535l.016-.08L1.13 11.31a2 2 0 0 1 .398-.76l.117-.128zm-.86 2.275-8.04 8.038a1 1 0 0 0-.215.321l-.042.123-.876 3.211 3.212-.876a1 1 0 0 0 .238-.1l.108-.071.098-.086 8.038-8.04zm4.089-1.568a1.784 1.784 0 0 0-2.402-.11l-.12.11-.86.86 2.52 2.522.861-.86a1.784 1.784 0 0 0 .11-2.402z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 538 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<defs><clipPath id="a"><path d="M2.1 0 16 13.9V0zM0 .7V16h15.3z"/></clipPath></defs>
|
||||
<path clip-path="url(#a)" d="m8.25 0-.154.006a2.249 2.249 0 0 0-2.074 1.939L6.014 2H3.502l-.145.006A1.5 1.5 0 0 0 2.002 3.5v2.264l-.058.006A2.25 2.25 0 0 0 0 8.001l.006.155a2.249 2.249 0 0 0 1.939 2.074l.056.005v2.266l.006.145A1.5 1.5 0 0 0 3.502 14h2.014l.006.055a2.25 2.25 0 0 0 2.23 1.943l.154-.006a2.25 2.25 0 0 0 2.074-1.94L9.986 14h2.516a1.5 1.5 0 0 0 1.5-1.5L14 9.247l-1.75.002-.123-.006A1.246 1.246 0 0 1 11 8l.006-.123A1.246 1.246 0 0 1 12.25 6.75L14 6.747l.002-3.246-.008-.144a1.499 1.499 0 0 0-1.492-1.355h-2.018l-.004-.057A2.252 2.252 0 0 0 8.25 0zm0 1c.69 0 1.25.56 1.25 1.25V3l3.002.002.09.008a.5.5 0 0 1 .41.492v2.246l-.752.002a2.25 2.25 0 0 0-2.242 2.066L10 7.98c0 1.194.903 2.167 2.066 2.262l.164.008.772-.002v2.254a.5.5 0 0 1-.5.5L9 13l.002.752a1.25 1.25 0 0 1-2.5-.002L6.5 13l-2.998.002a.5.5 0 0 1-.5-.5L3 9.25l-.752.002a1.249 1.249 0 1 1 .002-2.5L3 6.75l.002-3.248a.5.5 0 0 1 .5-.5L7 3v-.75C7 1.56 7.56 1 8.25 1z"/>
|
||||
<path d="M.5 0a.5.5 0 0 0-.354.146.5.5 0 0 0 0 .707l15 15a.5.5 0 0 0 .707 0 .5.5 0 0 0 0-.707l-15-15A.5.5 0 0 0 .499 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="m8.25 0-.154.006a2.249 2.249 0 0 0-2.074 1.939L6.014 2H3.502l-.145.006a1.5 1.5 0 0 0-1.355 1.494v2.264l-.058.006A2.25 2.25 0 0 0 0 8.001l.006.155a2.249 2.249 0 0 0 1.939 2.074l.056.005v2.266l.006.145A1.5 1.5 0 0 0 3.502 14h2.014l.006.055a2.25 2.25 0 0 0 2.23 1.943l.154-.006a2.25 2.25 0 0 0 2.074-1.94l.006-.052h2.516a1.5 1.5 0 0 0 1.5-1.5L14 9.247l-1.75.002-.123-.006A1.246 1.246 0 0 1 11 8l.006-.123A1.246 1.246 0 0 1 12.25 6.75L14 6.747l.002-3.246-.008-.144a1.499 1.499 0 0 0-1.492-1.355h-2.018l-.004-.057A2.252 2.252 0 0 0 8.25 0zm0 1c.69 0 1.25.56 1.25 1.25V3l3.002.002.09.008a.5.5 0 0 1 .41.492v2.246l-.752.002a2.25 2.25 0 0 0-2.242 2.066L10 7.98c0 1.194.903 2.167 2.066 2.262l.164.008.772-.002v2.254a.5.5 0 0 1-.5.5L9 13l.002.752a1.25 1.25 0 0 1-2.5-.002L6.5 13l-2.998.002a.5.5 0 0 1-.5-.5L3 9.25l-.752.002a1.249 1.249 0 1 1 .002-2.5L3 6.75l.002-3.248a.5.5 0 0 1 .5-.5L7 3v-.75C7 1.56 7.56 1 8.25 1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M5.5 7a.5.5 0 0 0-.5.5.5.5 0 0 0 .5.5h5a.5.5 0 0 0 .5-.5.5.5 0 0 0-.5-.5zM4 2c-.736 0-1.381.404-1.729 1h11.46c-.347-.596-.993-1-1.729-1zM3 4c-1.099 0-2 .9-2 2v7c0 1.099.9 2 2 2h10c1.099 0 2-.9 2-2V6c0-1.099-.9-2-2-2zm0 1h10c.563 0 1 .437 1 1v7c0 .563-.437 1-1 1H3c-.563 0-1-.437-1-1V6c0-.563.437-1 1-1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 444 B |