diff --git a/CLAUDE.md b/CLAUDE.md index 34e6dce..44870e5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,9 +17,10 @@ generated data live at: ~/.local/state/theme/schedule.json - Outcome of the last scheduled check ~/.cache/theme/backgrounds/ - Pre-rendered backgrounds per theme ~/.cache/theme/gowall/ - Per-theme gowall palettes, as JSON +~/.cache/theme/vivaldi/ - The theme staged for an open Vivaldi, and its waiter's pid ``` -Generated state belongs in those two cache directories and nowhere else. An app config here is +Generated state belongs in the cache directories above and nowhere else. An app config here is often a symlink into another repo, so writing generated state into it dirties that repo on every apply. @@ -59,9 +60,13 @@ beside the others, and an exported config is self-describing. Nothing is pruned, deliberately. These are also the directories a user's own themes live in, and an installer that deletes what it does not recognize is worse than a few stale kilobytes. -Four apps have no pointer at all. dunst reads `dunstrc.d/` as a directory, firefox and Windows -Terminal have their real file rewritten, and sioyek gets a managed block spliced into its own -config. +These apps have no pointer at all: + +- dunst reads `dunstrc.d/` as a directory. +- firefox and Windows Terminal have their real file rewritten. +- sioyek gets a managed block spliced into its own config. +- Vivaldi gets its theme upserted into each profile's `Preferences`, and only while Vivaldi is + closed. `tests/apply-install.bats` pins it, including the two migration cases off the copy-based scheme: a `current` left as a real file, and a `current.yazi` left as a real directory, which `ln -sfn` @@ -350,6 +355,20 @@ CLI verbs. These generators behave unlike the rest: - `aerc.sh` relies on aerc.conf pinning `styleset-name = current`, so the applied filename is a contract. - `firefox-based.sh` writes one userChrome.css covering Firefox, Zen, Librewolf and Thunderbird. - `vscode.sh` is not wired into `theme apply`; run it directly when needed. +- `vivaldi.sh` emits one entry of Vivaldi's `vivaldi.themes.user` pref, which + `lib/vivaldi-preferences.sh` upserts and selects. Vivaldi holds `Preferences` in memory and + writes all of it back on exit, so a write while it runs is lost. `apply_vivaldi` therefore + installs only into a closed Vivaldi. When Vivaldi is open, it stages the theme in + `~/.cache/theme/vivaldi/` and leaves a detached waiter, which installs the staged theme once + that Vivaldi's process exits. A staged theme with no waiter is one that never landed, and the + next apply warns about it. Nothing outside Vivaldi can change an open window's theme, so the + change shows at the next launch. Each dead end, with the command that re-checks it on Arch: + - CSS mods are linked once per window: `rg css-mods /opt/vivaldi/resources/vivaldi/window.html`. + - `--remote-debugging-port` is refused on the default profile: + `strings -a /opt/vivaldi/vivaldi-bin | rg 'non-default data directory'`. + - No enterprise policy maps to a `vivaldi.*` pref, so this prints `[]`: + `jq '[.policies[].path | select(startswith("vivaldi"))]' /opt/vivaldi/resources/vivaldi/policy_prefs.json`. + `BrowserThemeColor` tints Chromium's native toolbar, which Vivaldi does not draw with. - `glow.sh` emits a glamour style whose code blocks reach the screen quantized to xterm-256. glow never sets glamour's chroma formatter, so only prose keeps true color. `apply_glow` writes `glow.yml`'s `style` as an absolute path, because glamour expands neither `~` nor `$HOME` and diff --git a/README.md b/README.md index f0498fc..ecfef36 100644 --- a/README.md +++ b/README.md @@ -140,11 +140,16 @@ Restart the terminal to see opacity changes. theme browsers # Show detected browsers and theme status ``` -Firefox-based browsers (Zen, Librewolf, Firefox, Thunderbird) and Chromium are -themed automatically on `theme apply`. Firefox-based browsers require +Firefox-based browsers and Vivaldi are themed automatically on `theme apply`. +Firefox-based browsers require `toolkit.legacyUserProfileCustomizations.stylesheets = true` in `about:config`, then a restart, for `userChrome.css` to take effect. +Vivaldi gets a native Vivaldi theme, selected for both its light and dark +schedule. Vivaldi rewrites its own settings when it quits, so an apply while it +is open waits for it to exit and installs the theme then. The theme shows at its +next launch. + ### Cross-Machine Sync Sync your theme preferences across machines using a GitHub Gist: @@ -197,7 +202,7 @@ Both commands come from [`bashselfupdate`](https://github.com/datapointchris/bas - **yazi** — file manager - **sioyek** — PDF viewer (custom color mode) - **Neovim** — text editor (via colorscheme plugins or generated) -- **Browsers** — Firefox-based (Zen, Librewolf, Firefox, Thunderbird) and Chromium +- **Browsers** — Firefox-based (Zen, Librewolf, Firefox, Thunderbird) and Vivaldi - **Wallpaper** — themed desktop background, set through Finder on macOS, hyprpaper on Hyprland, and the Windows host on WSL diff --git a/bin/theme b/bin/theme index a0968a0..7f6ca07 100755 --- a/bin/theme +++ b/bin/theme @@ -576,7 +576,6 @@ browsers_usage() { print_section "Description" "blue" cat <<'EOF' Show detected browsers and their theme configuration status. - Supports Firefox-based browsers: Zen, Librewolf, Firefox, Thunderbird. EOF print_section "Setup Required" "blue" @@ -600,7 +599,6 @@ Browser Theme Diagnostics Description ----------- Show detected browsers and their theme configuration status. - Supports Firefox-based browsers: Zen, Librewolf, Firefox, Thunderbird. Setup Required -------------- @@ -840,6 +838,20 @@ show_browsers() { echo " toolkit.legacyUserProfileCustomizations.stylesheets = true" echo " Then restart the browser." + local vivaldi_dir + vivaldi_dir=$(vivaldi_user_data_dir) + if [[ -f "$vivaldi_dir/Local State" ]]; then + echo "" + echo "Vivaldi:" + vivaldi_selected_themes "$vivaldi_dir" | sed 's/^/ /' + local vivaldi_stranded + if vivaldi_stranded=$(vivaldi_stranded_theme); then + echo " Pending: $vivaldi_stranded, never installed. The next theme apply replaces it." + elif [[ -f "$VIVALDI_PENDING_THEME" ]]; then + echo " Pending: $(jq -r '.name // .id' "$VIVALDI_PENDING_THEME"), installed when Vivaldi exits" + fi + fi + # Show chromium status if chromium.theme exists local chromium_theme_file="$HOME/.config/chromium/chromium.theme" if [[ -f "$chromium_theme_file" ]]; then diff --git a/lib/generate-all.sh b/lib/generate-all.sh index 6789b5a..5324cb5 100755 --- a/lib/generate-all.sh +++ b/lib/generate-all.sh @@ -52,6 +52,7 @@ declare -A GENERATOR_OUTPUT=( ["icons"]="icons.theme" ["hyprland-picker"]="hyprland-picker.css" ["chromium"]="chromium.theme" + ["vivaldi"]="vivaldi.json" ["firefox-based"]="userChrome.css" ["bat"]="bat.tmTheme" ["sioyek"]="sioyek.config" diff --git a/lib/generators/vivaldi.sh b/lib/generators/vivaldi.sh new file mode 100755 index 0000000..f5f182d --- /dev/null +++ b/lib/generators/vivaldi.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# Generate a Vivaldi theme object from theme.yml +# Usage: vivaldi.sh [output-file] +# +# The object is one entry of Vivaldi's `vivaldi.themes.user` pref, the same +# shape a theme archive's settings.json carries. `theme apply` upserts it into +# the profile's Preferences; see lib/vivaldi-preferences.sh. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/../theme.sh" + +if [[ $# -lt 1 ]]; then + echo "Usage: $0 [output-file]" + exit 1 +fi + +input_file="$1" +output_file="${2:-}" + +eval "$(load_colors "$input_file")" + +UI_ACCENT="${EXTENDED_UI_ACCENT:-$BASE0D}" +UI_SELECTION="${EXTENDED_UI_SELECTION:-$BASE02}" + +# Vivaldi derives every shade, hover and contrast text from the colors below, +# so the palette's own intermediate slots have nowhere to go. +# +# Vivaldi paints its accent across the whole toolbar row and the active tab, so +# the accent takes the quiet UI selection shade. The loud UI accent goes to +# highlight, which marks focus and selection. +generate() { + jq -n \ + --arg id "theme-${THEME_SLUG}" \ + --arg name "$THEME_NAME" \ + --arg bg "$SPECIAL_BG" \ + --arg fg "$SPECIAL_FG" \ + --arg window "$BASE01" \ + --arg accent "$UI_SELECTION" \ + --arg highlight "$UI_ACCENT" \ + '{ + engineVersion: 1, + version: 1, + id: $id, + url: "", + name: $name, + accentFromPage: false, + accentOnWindow: false, + accentSaturationLimit: 1, + backgroundImage: "", + backgroundPosition: "stretch", + backgroundSource: "", + radius: 4, + colorAccentBg: $accent, + colorBg: $bg, + colorFg: $fg, + colorHighlightBg: $highlight, + colorWindowBg: $window, + contrast: 0, + preferSystemAccent: false, + alpha: 1, + blur: 0, + transparencyTabs: false, + transparencyTabBar: false + }' +} + +if [[ -n "$output_file" ]]; then + generate >"$output_file" + echo "Generated: $output_file" +else + generate +fi diff --git a/lib/lib.sh b/lib/lib.sh index 86aacaf..3a44e32 100755 --- a/lib/lib.sh +++ b/lib/lib.sh @@ -16,6 +16,7 @@ THEME_APP_DIR="$(cd "$THEME_LIB_DIR/.." && pwd)" # the other, and on Arch that silently skipped Hyprland, Waybar, ghostty, kitty # and the wallpaper. storage.sh also owns THEME_STATE_DIR and the history paths. source "$THEME_LIB_DIR/storage.sh" +source "$THEME_LIB_DIR/vivaldi-preferences.sh" # Always production, unlike THEME_STATE_DIR: apps watch this directory, so dev # mode must not point them at .dev-data. @@ -45,6 +46,7 @@ THEME_APP_ARTIFACTS=( "sioyek.config:sioyek" "userChrome.css:Firefox-based" "chromium.theme:Chromium" + "vivaldi.json:Vivaldi" "bordersrc:JankyBorders" "hyprland.lua:Hyprland" "hyprlock.conf:Hyprlock" @@ -620,6 +622,32 @@ apply_firefox_based() { return 0 } +# Apply Vivaldi theme (macOS and Arch) +# Prints "at next launch" when Vivaldi is open, and nothing when installed. +apply_vivaldi() { + local theme="$1" + local lib_path + lib_path=$(get_library_path "$theme") + + if [[ -z "$lib_path" ]] || [[ ! -f "$lib_path/vivaldi.json" ]]; then + return 1 + fi + + local dir + dir=$(vivaldi_user_data_dir) + [[ -f "$dir/Local State" ]] || return 1 + + if vivaldi_running_pid "$dir" >/dev/null; then + vivaldi_stage_theme "$lib_path/vivaldi.json" || return 1 + vivaldi_waiter_running || start_detached "$THEME_LIB_DIR/vivaldi-install-after-exit.sh" "$dir" + echo "at next launch" + return 0 + fi + + vivaldi_install_theme "$dir" "$lib_path/vivaldi.json" || return 1 + rm -f "$VIVALDI_PENDING_THEME" +} + # Apply sioyek PDF viewer theme (all platforms) # Splices a marker-delimited managed block into the user's prefs_user.config. # @@ -2121,6 +2149,22 @@ apply_theme_to_apps() { _print_app_status "browsers" "false" fi + # Vivaldi (macOS and Arch) + if [[ "$platform" == "macos" ]] || [[ "$platform" == "arch" ]]; then + local vivaldi_stranded + if vivaldi_stranded=$(vivaldi_stranded_theme); then + apply_warn "Vivaldi never got $vivaldi_stranded: it was still open when its waiter stopped. This apply replaces it." + fi + local vivaldi_status + if vivaldi_status=$(apply_vivaldi "$theme" 2>/dev/null); then + applied+=("vivaldi") + _print_app_status "vivaldi${vivaldi_status:+ ($vivaldi_status)}" "true" + else + skipped+=("vivaldi") + _print_app_status "vivaldi" "false" + fi + fi + # bat syntax highlighter (all platforms) if apply_bat "$theme" 2>/dev/null; then applied+=("bat") @@ -2405,18 +2449,28 @@ reload_dunst() { return 1 } -# Starts a desktop daemon outside the apply's cgroup, which systemd kills when an apply run as a -# service ends. Hyprland's child sits where login started it. A transient scope is the fallback. +# Starts a desktop daemon outside the apply's cgroup. Hyprland's child sits where login started +# it, so the daemon lives as if login had started it. start_detached is the fallback. start_session_daemon() { local program="$1" if command -v hyprctl &>/dev/null \ && [[ "$(hyprctl dispatch "hl.dsp.exec_cmd('$program')" 2>/dev/null)" == "ok" ]]; then return 0 fi + start_detached "$program" +} + +# Starts a command that must outlive the apply. systemd kills a scheduled apply's cgroup when the +# oneshot exits, so where a systemd user manager runs, the command gets its own transient scope. +# Elsewhere it gets its own session. On macOS the launchd agent sets AbandonProcessGroup +# (lib/schedule.sh), which spares it. +start_detached() { if systemctl --user show-environment &>/dev/null; then - systemd-run --user --scope --collect --quiet "$program" >/dev/null 2>&1 /dev/null 2>&1 /dev/null; then + setsid "$@" >/dev/null 2>&1 /dev/null 2>&1 /dev/null 2>&1 + +set -euo pipefail + +source "$(dirname "${BASH_SOURCE[0]}")/vivaldi-preferences.sh" + +vivaldi_install_after_exit "$1" diff --git a/lib/vivaldi-preferences.sh b/lib/vivaldi-preferences.sh new file mode 100755 index 0000000..84857b9 --- /dev/null +++ b/lib/vivaldi-preferences.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash +# Reads and writes Vivaldi's own profile. +# +# Vivaldi keeps its themes in each profile's Preferences and holds that file in +# memory while it runs, writing all of it back on exit. A write made while it is +# open is overwritten, so a theme is installed only while Vivaldi is closed. An +# apply that finds it open stages the theme as pending and leaves a waiter, +# which installs the pending theme once that Vivaldi exits. +# +# The waiter sources only this file, so nothing here reads lib.sh. + +VIVALDI_STATE_DIR="$HOME/.cache/theme/vivaldi" +VIVALDI_PENDING_THEME="$VIVALDI_STATE_DIR/pending-theme.json" +VIVALDI_WAITER_PID_FILE="$VIVALDI_STATE_DIR/waiter.pid" + +vivaldi_user_data_dir() { + case "$(uname -s)" in + Darwin) echo "$HOME/Library/Application Support/Vivaldi" ;; + *) echo "${XDG_CONFIG_HOME:-$HOME/.config}/vivaldi" ;; + esac +} + +# A lock or pid file outlives the process that wrote it, and kill -0 answers for +# whatever process holds the number now, so a pid is trusted only while it still +# runs the expected program. +vivaldi_pid_is_vivaldi() { + local pid="$1" comm + [[ "$pid" =~ ^[0-9]+$ ]] || return 1 + comm=$(ps -p "$pid" -o comm= 2>/dev/null) || return 1 + comm="${comm##*/}" + [[ "$comm" == vivaldi-bin || "$comm" == Vivaldi ]] +} + +# The pid of the Vivaldi holding this user-data dir, read from Chromium's own +# SingletonLock symlink, whose target is `-`. The host is not +# compared, because macOS renames the host with the network while the lock +# keeps the name from launch. +vivaldi_running_pid() { + local target pid + target=$(readlink "$1/SingletonLock" 2>/dev/null) || return 1 + pid="${target##*-}" + vivaldi_pid_is_vivaldi "$pid" || return 1 + echo "$pid" +} + +# Each profile directory with its display name, tab-separated, from Local +# State, which is Vivaldi's own record of its profiles. +vivaldi_profiles() { + jq -r '.profile.info_cache // {} | to_entries[] | [.key, (.value.name // .key)] | @tsv' "$1/Local State" +} + +# Upsert one theme object into every profile's Preferences and select it. +# +# Both OS-schedule slots are pointed at it as well as `current`, because +# Vivaldi's default schedule follows the OS light/dark setting and reads those +# slots instead of `current`. Every other user theme stays installed. A profile +# that fails leaves the rest to be installed, and the call returns 1. +vivaldi_install_theme() { + local dir="$1" + local theme_json="$2" + + [[ -f "$dir/Local State" && -f "$theme_json" ]] || return 1 + + local profiles + profiles=$(vivaldi_profiles "$dir") || return 1 + + local profile name prefs staged failed=0 + while IFS=$'\t' read -r profile name; do + [[ -n "$profile" ]] || continue + prefs="$dir/$profile/Preferences" + [[ -f "$prefs" ]] || continue + + # Staged beside the file so the mv is a rename; mktemp's 0600 matches + # Chromium's own mode for it. + if ! staged=$(mktemp "$prefs.theme.XXXXXX"); then + failed=1 + continue + fi + if jq -c --slurpfile theme "$theme_json" ' + $theme[0] as $t + | .vivaldi.themes.user = ([(.vivaldi.themes.user // [])[] | select(.id != $t.id)] + [$t]) + | .vivaldi.themes.current = $t.id + | .vivaldi.theme.schedule.o_s = ((.vivaldi.theme.schedule.o_s // {}) + {light: $t.id, dark: $t.id}) + ' "$prefs" >"$staged" && mv "$staged" "$prefs"; then + continue + fi + rm -f "$staged" + failed=1 + done <<<"$profiles" + + return "$failed" +} + +# What each profile shows, one `: ` line apiece. The +# schedule decides which pref that is: `off` reads `current`, the default +# `system` reads the OS light and dark slots, and `timeline` reads a list of +# times this tool never writes. Absent prefs take Vivaldi's own defaults. +vivaldi_selected_themes() { + local dir="$1" profile name + while IFS=$'\t' read -r profile name; do + [[ -n "$profile" ]] || continue + jq -r --arg profile "$name" ' + ((.vivaldi.themes.user // []) | map({key: .id, value: .name}) | from_entries) as $names + | (.vivaldi.theme.schedule // {}) as $s + | ($s.o_s // {}) as $o + | ($o.light // "Vivaldi1") as $light + | ($o.dark // "Vivaldi2") as $dark + | ($s.enabled // "system") as $mode + | if $mode == "off" or $mode == 0 then ($names[.vivaldi.themes.current // "Vivaldi1"] // (.vivaldi.themes.current // "Vivaldi1")) + elif $mode == "timeline" or $mode == 2 then "a timeline schedule" + elif $light == $dark then ($names[$light] // $light) + else "\($names[$light] // $light) when light, \($names[$dark] // $dark) when dark" + end + | "\($profile): \(.)" + ' "$dir/$profile/Preferences" 2>/dev/null || echo "$name: unreadable" + done < <(vivaldi_profiles "$dir") +} + +# The waiter is trusted only while its pid still runs the waiter. +vivaldi_waiter_running() { + local pid args + pid=$(cat "$VIVALDI_WAITER_PID_FILE" 2>/dev/null) || return 1 + [[ "$pid" =~ ^[0-9]+$ ]] || return 1 + args=$(ps -p "$pid" -o args= 2>/dev/null) || return 1 + [[ "$args" == *vivaldi-install-after-exit* ]] +} + +# Stage a theme for the waiter. A later apply overwrites it, so the theme that +# lands is the last one applied while Vivaldi was open. +vivaldi_stage_theme() { + mkdir -p "$VIVALDI_STATE_DIR" || return 1 + cp "$1" "$VIVALDI_PENDING_THEME.tmp" && mv "$VIVALDI_PENDING_THEME.tmp" "$VIVALDI_PENDING_THEME" +} + +# The name of a staged theme no waiter will install. That is what a waiter +# leaves when its install failed, or when Vivaldi outlasted it at logout. +vivaldi_stranded_theme() { + [[ -f "$VIVALDI_PENDING_THEME" ]] || return 1 + vivaldi_waiter_running && return 1 + jq -r '.name // .id' "$VIVALDI_PENDING_THEME" 2>/dev/null || echo "a theme" +} + +# The waiter's body. It waits for the whole process to exit rather than for the +# lock to go, since Chromium may drop the lock before its last Preferences write. +# A Vivaldi relaunched within the poll interval already read the old file, so +# the wait moves on to that instance. A theme staged during the install is +# installed on the next pass. +# +# A logout or reboot sends SIGTERM to Vivaldi and to this at once. Vivaldi takes +# a moment to write and exit, so the TERM is answered with a bounded wait rather +# than by dying first. A failure leaves the theme staged, which the next apply +# reports. +vivaldi_install_after_exit() { + local dir="$1" + local claimed="$VIVALDI_STATE_DIR/installing-theme.json" + + mkdir -p "$VIVALDI_STATE_DIR" + echo "$$" >"$VIVALDI_WAITER_PID_FILE" + trap 'rm -f "$VIVALDI_WAITER_PID_FILE"' EXIT + + local deadline="" + trap 'deadline=$((SECONDS + 15))' TERM HUP + + local pid + while [[ -f "$VIVALDI_PENDING_THEME" ]]; do + while pid=$(vivaldi_running_pid "$dir"); do + while vivaldi_pid_is_vivaldi "$pid"; do + if [[ -n "$deadline" ]] && ((SECONDS >= deadline)); then + echo "Vivaldi outlasted the session; the staged theme stays pending" >&2 + return 1 + fi + sleep 1 + done + done + + mv "$VIVALDI_PENDING_THEME" "$claimed" || return 1 + if ! vivaldi_install_theme "$dir" "$claimed"; then + [[ -f "$VIVALDI_PENDING_THEME" ]] || mv "$claimed" "$VIVALDI_PENDING_THEME" + echo "Could not install the staged theme into $dir" >&2 + return 1 + fi + rm -f "$claimed" + done +} diff --git a/tests/vivaldi-preferences.bats b/tests/vivaldi-preferences.bats new file mode 100755 index 0000000..588cee9 --- /dev/null +++ b/tests/vivaldi-preferences.bats @@ -0,0 +1,37 @@ +#!/usr/bin/env bats +# Installing a theme into Vivaldi's Preferences. +# +# The file is the user's whole browser profile, rewritten in place, so an apply +# must change nothing but the theme: their own themes and every other pref +# survive, a reapplied theme is replaced rather than duplicated, and ours is +# selected under both OS-schedule slots as well as `current`. + +load "$HOME/.local/lib/bats-support/load.bash" +load "$HOME/.local/lib/bats-assert/load.bash" + +setup() { + load "$BATS_TEST_DIRNAME/helpers.bash" + isolate_theme_state + source_theme_libs + + vivaldi="$BATS_TEST_TMPDIR/vivaldi" + mkdir -p "$vivaldi/Default" + printf '{"profile":{"info_cache":{"Default":{}}}}' >"$vivaldi/Local State" + printf '{"browser":{"has_seen_welcome_page":true},"vivaldi":{"theme":{"schedule":{"o_s":{"light":"Vivaldi1","dark":"Vivaldi2"}}},"themes":{"user":[{"id":"mine","name":"Mine"}]}}}' \ + >"$vivaldi/Default/Preferences" + printf '{"id":"theme-a","name":"A"}' >"$BATS_TEST_TMPDIR/a.json" + printf '{"id":"theme-b","name":"B"}' >"$BATS_TEST_TMPDIR/b.json" +} + +@test "an install keeps the user's themes and prefs and selects the theme" { + vivaldi_install_theme "$vivaldi" "$BATS_TEST_TMPDIR/a.json" + vivaldi_install_theme "$vivaldi" "$BATS_TEST_TMPDIR/b.json" + vivaldi_install_theme "$vivaldi" "$BATS_TEST_TMPDIR/b.json" + + run jq -c '[.vivaldi.themes.user[].id], .vivaldi.themes.current, .vivaldi.theme.schedule.o_s, .browser' \ + "$vivaldi/Default/Preferences" + assert_output '["mine","theme-a","theme-b"] +"theme-b" +{"light":"theme-b","dark":"theme-b"} +{"has_seen_welcome_page":true}' +} diff --git a/themes/cendre-medium/vivaldi.json b/themes/cendre-medium/vivaldi.json new file mode 100644 index 0000000..f8dba63 --- /dev/null +++ b/themes/cendre-medium/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-cendre-medium", + "url": "", + "name": "Cendre Medium", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#36241d", + "colorBg": "#1d1917", + "colorFg": "#e6d5c2", + "colorHighlightBg": "#ea9875", + "colorWindowBg": "#26211f", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/cendre-soft/vivaldi.json b/themes/cendre-soft/vivaldi.json new file mode 100644 index 0000000..352790d --- /dev/null +++ b/themes/cendre-soft/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-cendre-soft", + "url": "", + "name": "Cendre Soft", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#3d2b23", + "colorBg": "#231f1d", + "colorFg": "#e6d5c2", + "colorHighlightBg": "#ea9875", + "colorWindowBg": "#2d2725", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/cendre/vivaldi.json b/themes/cendre/vivaldi.json new file mode 100644 index 0000000..3b42126 --- /dev/null +++ b/themes/cendre/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-cendre", + "url": "", + "name": "Cendre", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#2f1e17", + "colorBg": "#171311", + "colorFg": "#e6d5c2", + "colorHighlightBg": "#ea9875", + "colorWindowBg": "#201b19", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/charcoal-ember/vivaldi.json b/themes/charcoal-ember/vivaldi.json new file mode 100644 index 0000000..472f3e9 --- /dev/null +++ b/themes/charcoal-ember/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-charcoal-ember", + "url": "", + "name": "Charcoal Ember", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#373b41", + "colorBg": "#1d1f21", + "colorFg": "#c5c8c6", + "colorHighlightBg": "#4eb6fe", + "colorWindowBg": "#2d2d2d", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/everforest-dark-hard/vivaldi.json b/themes/everforest-dark-hard/vivaldi.json new file mode 100644 index 0000000..2100853 --- /dev/null +++ b/themes/everforest-dark-hard/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-everforest-dark-hard", + "url": "", + "name": "Everforest Dark Hard", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#4c3743", + "colorBg": "#1e2326", + "colorFg": "#d3c6aa", + "colorHighlightBg": "#a7c080", + "colorWindowBg": "#2e383c", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/flexoki-moon-black/vivaldi.json b/themes/flexoki-moon-black/vivaldi.json new file mode 100644 index 0000000..4594c15 --- /dev/null +++ b/themes/flexoki-moon-black/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-flexoki-moon-black", + "url": "", + "name": "Flexoki Moon Black", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#343331", + "colorBg": "#100f0f", + "colorFg": "#cecdc3", + "colorHighlightBg": "#3aa99f", + "colorWindowBg": "#1c1b1a", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/flexoki-moon-green/vivaldi.json b/themes/flexoki-moon-green/vivaldi.json new file mode 100644 index 0000000..f786391 --- /dev/null +++ b/themes/flexoki-moon-green/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-flexoki-moon-green", + "url": "", + "name": "Flexoki Moon Green", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#2c3b2d", + "colorBg": "#08120b", + "colorFg": "#ddd8e8", + "colorHighlightBg": "#3aa99f", + "colorWindowBg": "#151f18", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/flexoki-moon-purple/vivaldi.json b/themes/flexoki-moon-purple/vivaldi.json new file mode 100644 index 0000000..8efea45 --- /dev/null +++ b/themes/flexoki-moon-purple/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-flexoki-moon-purple", + "url": "", + "name": "Flexoki Moon Purple", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#252330", + "colorBg": "#12101d", + "colorFg": "#ddd8e8", + "colorHighlightBg": "#3aa99f", + "colorWindowBg": "#1c1a25", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/flexoki-moon-red/vivaldi.json b/themes/flexoki-moon-red/vivaldi.json new file mode 100644 index 0000000..4de994c --- /dev/null +++ b/themes/flexoki-moon-red/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-flexoki-moon-red", + "url": "", + "name": "Flexoki Moon Red", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#42322e", + "colorBg": "#18090b", + "colorFg": "#f0e8dd", + "colorHighlightBg": "#3aa99f", + "colorWindowBg": "#251a1a", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/gruvbox/vivaldi.json b/themes/gruvbox/vivaldi.json new file mode 100644 index 0000000..c7b03b3 --- /dev/null +++ b/themes/gruvbox/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-gruvbox", + "url": "", + "name": "Gruvbox", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#504945", + "colorBg": "#1d2021", + "colorFg": "#ebdbb2", + "colorHighlightBg": "#83a598", + "colorWindowBg": "#3c3836", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/kanagawa/vivaldi.json b/themes/kanagawa/vivaldi.json new file mode 100644 index 0000000..254b8c0 --- /dev/null +++ b/themes/kanagawa/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-kanagawa", + "url": "", + "name": "Kanagawa", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#2d4f67", + "colorBg": "#1f1f28", + "colorFg": "#dcd7ba", + "colorHighlightBg": "#7e9cd8", + "colorWindowBg": "#16161d", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/nightfox/vivaldi.json b/themes/nightfox/vivaldi.json new file mode 100644 index 0000000..f8244e7 --- /dev/null +++ b/themes/nightfox/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-nightfox", + "url": "", + "name": "Nightfox", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#2b3b51", + "colorBg": "#192330", + "colorFg": "#cdcecf", + "colorHighlightBg": "#719cd6", + "colorWindowBg": "#212e3f", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/nordic/vivaldi.json b/themes/nordic/vivaldi.json new file mode 100644 index 0000000..7140593 --- /dev/null +++ b/themes/nordic/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-nordic", + "url": "", + "name": "Nordic", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#3B4252", + "colorBg": "#242933", + "colorFg": "#BBC3D4", + "colorHighlightBg": "#88C0D0", + "colorWindowBg": "#2E3440", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/oceanic-next/vivaldi.json b/themes/oceanic-next/vivaldi.json new file mode 100644 index 0000000..8a6c0d2 --- /dev/null +++ b/themes/oceanic-next/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-oceanic-next", + "url": "", + "name": "Oceanic Next", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#4f5b66", + "colorBg": "#162c35", + "colorFg": "#c0c5ce", + "colorHighlightBg": "#6699cc", + "colorWindowBg": "#65737e", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/popping-and-locking/vivaldi.json b/themes/popping-and-locking/vivaldi.json new file mode 100644 index 0000000..3d145e8 --- /dev/null +++ b/themes/popping-and-locking/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-popping-and-locking", + "url": "", + "name": "Popping and Locking", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#383637", + "colorBg": "#181921", + "colorFg": "#ebdbb2", + "colorHighlightBg": "#458588", + "colorWindowBg": "#252631", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/retrobox/vivaldi.json b/themes/retrobox/vivaldi.json new file mode 100644 index 0000000..6326ea5 --- /dev/null +++ b/themes/retrobox/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-retrobox", + "url": "", + "name": "Retrobox", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#504945", + "colorBg": "#1c1c1c", + "colorFg": "#ebdbb2", + "colorHighlightBg": "#83a598", + "colorWindowBg": "#3c3836", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/rose-pine/vivaldi.json b/themes/rose-pine/vivaldi.json new file mode 100644 index 0000000..2e37bbf --- /dev/null +++ b/themes/rose-pine/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-rose-pine", + "url": "", + "name": "Rose Pine", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#403d52", + "colorBg": "#191724", + "colorFg": "#e0def4", + "colorHighlightBg": "#c4a7e7", + "colorWindowBg": "#1f1d2e", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/solarized-osaka/vivaldi.json b/themes/solarized-osaka/vivaldi.json new file mode 100644 index 0000000..055c95e --- /dev/null +++ b/themes/solarized-osaka/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-solarized-osaka", + "url": "", + "name": "Solarized Osaka", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#073642", + "colorBg": "#001419", + "colorFg": "#839395", + "colorHighlightBg": "#268bd3", + "colorWindowBg": "#002b36", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/sora/vivaldi.json b/themes/sora/vivaldi.json new file mode 100644 index 0000000..0c9a57c --- /dev/null +++ b/themes/sora/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-sora", + "url": "", + "name": "Sora", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#1e2430", + "colorBg": "#0e1018", + "colorFg": "#c8d0e0", + "colorHighlightBg": "#80c8e0", + "colorWindowBg": "#14161e", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +} diff --git a/themes/terafox/vivaldi.json b/themes/terafox/vivaldi.json new file mode 100644 index 0000000..91d7b64 --- /dev/null +++ b/themes/terafox/vivaldi.json @@ -0,0 +1,25 @@ +{ + "engineVersion": 1, + "version": 1, + "id": "theme-terafox", + "url": "", + "name": "Terafox", + "accentFromPage": false, + "accentOnWindow": false, + "accentSaturationLimit": 1, + "backgroundImage": "", + "backgroundPosition": "stretch", + "backgroundSource": "", + "radius": 4, + "colorAccentBg": "#293e40", + "colorBg": "#152528", + "colorFg": "#e6eaea", + "colorHighlightBg": "#5a93aa", + "colorWindowBg": "#1d3337", + "contrast": 0, + "preferSystemAccent": false, + "alpha": 1, + "blur": 0, + "transparencyTabs": false, + "transparencyTabBar": false +}