diff --git a/bin/omarchy-install-ai-t3-code b/bin/omarchy-install-ai-t3-code new file mode 100755 index 00000000..b9e3af38 --- /dev/null +++ b/bin/omarchy-install-ai-t3-code @@ -0,0 +1,31 @@ +#!/bin/bash + +# omarchy:summary=Install T3 Code and point it at the Omarchy palette +# omarchy:requires-sudo=true + +set -e + +T3CODE_HOME="${T3CODE_HOME:-$HOME/.t3}" +T3CODE_STATE_DIR="$T3CODE_HOME/userdata" + +echo "Installing T3 Code..." +omarchy-pkg-add t3code-bin + +echo "Matching T3 Code to the current theme..." +mkdir -p "$T3CODE_STATE_DIR" + +# An updated user's current theme may have been staged before this template existed. +if [[ ! -f $HOME/.local/state/omarchy/current/theme/t3code.json ]]; then + omarchy-theme-refresh +fi + +omarchy-theme-set-t3code + +# The packaged CLI selects the published palette before the app's first launch. +t3 theme set omarchy --base-dir "$T3CODE_HOME" + +echo "Opening T3 Code..." +setsid uwsm-app -- gtk-launch t3code >/dev/null 2>&1 & + +echo "" +echo "T3 Code has been installed." diff --git a/bin/omarchy-theme-set b/bin/omarchy-theme-set index 614a8a1b..782b4ba7 100755 --- a/bin/omarchy-theme-set +++ b/bin/omarchy-theme-set @@ -372,6 +372,7 @@ post_theme_commands=( omarchy-theme-set-pi omarchy-theme-set-claude omarchy-theme-set-hermes + omarchy-theme-set-t3code omarchy-theme-set-browser omarchy-theme-set-vscode omarchy-theme-set-obsidian diff --git a/bin/omarchy-theme-set-t3code b/bin/omarchy-theme-set-t3code new file mode 100755 index 00000000..7b6f69bf --- /dev/null +++ b/bin/omarchy-theme-set-t3code @@ -0,0 +1,34 @@ +#!/bin/bash + +# omarchy:summary=Sync the generated Omarchy theme to T3 Code +# omarchy:hidden=true + +set -euo pipefail + +T3CODE_SOURCE_PATH="$HOME/.local/state/omarchy/current/theme/t3code.json" +T3CODE_HOME="${T3CODE_HOME:-$HOME/.t3}" +T3CODE_STATE_DIR="$T3CODE_HOME/userdata" +# The filename is the theme id T3 Code shows and `t3 theme set` accepts, and +# it stays stable while the colors change underneath on every theme switch. +T3CODE_THEME_PATH="$T3CODE_STATE_DIR/themes/omarchy.json" + +[[ -f $T3CODE_SOURCE_PATH ]] || exit 0 + +# A theme without an accent or a background leaves its placeholder unresolved. +# Publishing that would only make T3 Code reject the file, so skip instead and +# leave the previous palette in place. +if grep -q '{{' "$T3CODE_SOURCE_PATH"; then + echo "Skipping T3 Code theme: $(basename "$T3CODE_SOURCE_PATH") has unresolved colors." >&2 + exit 0 +fi + +# Only follow an install that already exists. Creating the directory here would +# leave a stray T3 Code state dir on machines that do not run it. +[[ -d $T3CODE_STATE_DIR ]] || exit 0 + +# T3 Code watches this file and retints every connected client on change, so +# the write has to be atomic: a half-written file would read as invalid. +mkdir -p "$(dirname "$T3CODE_THEME_PATH")" +tmp=$(mktemp "$T3CODE_THEME_PATH.XXXXXX") +cp "$T3CODE_SOURCE_PATH" "$tmp" +mv "$tmp" "$T3CODE_THEME_PATH" diff --git a/default/omarchy/omarchy-menu.jsonc b/default/omarchy/omarchy-menu.jsonc index 0e590652..745ec311 100644 --- a/default/omarchy/omarchy-menu.jsonc +++ b/default/omarchy/omarchy-menu.jsonc @@ -248,7 +248,7 @@ "install.ai.ollama": {"icon":"","iconFont":"omarchy","label":"Ollama","disabled":"omarchy-cmd-present ollama","action":"if omarchy-cmd-present nvidia-smi; then ollama_pkg=ollama-cuda; elif omarchy-cmd-present rocminfo; then ollama_pkg=ollama-rocm; else ollama_pkg=ollama; fi; omarchy-install-app Ollama \"$ollama_pkg\""}, "install.ai.openclaw": {"icon":"","iconFont":"omarchy","label":"OpenClaw","disabled":"omarchy-pkg-present openclaw","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-openclaw"}, "install.ai.perplexity": {"icon":"","iconFont":"omarchy","label":"Perplexity","disabled":"omarchy-pkg-present perplexity","action":"omarchy-install-and-launch Perplexity perplexity perplexity"}, - "install.ai.t3-code": {"icon":"","iconFont":"omarchy","label":"T3 Code","disabled":"omarchy-pkg-present t3code-bin","action":"omarchy-install-and-launch 'T3 Code' t3code-bin t3code"}, + "install.ai.t3-code": {"icon":"","iconFont":"omarchy","label":"T3 Code","disabled":"omarchy-pkg-present t3code-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-t3-code"}, "install.gaming.steam": {"icon":"","label":"Steam","disabled":"omarchy-pkg-present steam","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-steam"}, "install.gaming.retroarch": {"icon":"󰯉","label":"RetroArch","disabled":"omarchy-pkg-present retroarch","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-retroarch"}, "install.gaming.minecraft": {"icon":"󰍳","label":"Minecraft","disabled":"omarchy-pkg-present minecraft-launcher","action":"omarchy-install-and-launch Minecraft minecraft-launcher minecraft-launcher"}, diff --git a/default/themed/t3code.json.tpl b/default/themed/t3code.json.tpl new file mode 100644 index 00000000..e420dcc4 --- /dev/null +++ b/default/themed/t3code.json.tpl @@ -0,0 +1,36 @@ +{ + "name": "Omarchy", + "appearance": "{{ mode }}", + "canvas": "{{ background }}", + "accent": "{{ accent }}", + "colors": { + "chrome": "{{ background }}", + "toolbar": "{{ background }}", + "toolbarForeground": "{{ foreground }}", + "toolbarBorder": "{{ muted }}", + + "text": "{{ foreground }}", + "border": "{{ muted }}", + "focus": "{{ accent }}", + + "sidebar": "{{ dark_background }}", + "sidebarForeground": "{{ foreground }}", + "sidebarBorder": "{{ muted }}", + "sidebarRowHover": "{{ mix dark_background foreground 6% }}", + "sidebarRowActive": "{{ mix dark_background accent 18% }}", + "sidebarRowSelected": "{{ selection }}", + + "error": "{{ mix red foreground 35% }}", + "warning": "{{ mix yellow foreground 35% }}", + + "codeBackground": "{{ dark_background }}", + "codeForeground": "{{ foreground }}", + + "terminalBackground": "{{ background }}", + "terminalForeground": "{{ foreground }}", + "terminalCursor": "{{ bright_foreground }}", + "terminalSelection": "{{ selection }}", + "terminalScrollbar": "{{ muted }}", + "terminalScrollbarHover": "{{ dark_foreground }}" + } +} diff --git a/test/shell.d/t3code-install-test.sh b/test/shell.d/t3code-install-test.sh new file mode 100755 index 00000000..66bc9ecd --- /dev/null +++ b/test/shell.d/t3code-install-test.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT + +test_home="$test_tmp/home" +mock_bin="$test_tmp/bin" +mkdir -p "$mock_bin" + +# Run the real theme refresh, renderer and publisher in a throwaway home. +# Only package installation, the T3 CLI and desktop launch are stubbed. +cat >"$mock_bin/omarchy-pkg-add" <<'SH' +#!/bin/bash +[[ $1 == "t3code-bin" ]] +SH + +cat >"$mock_bin/t3" <<'SH' +#!/bin/bash +[[ $1 == "theme" && $2 == "set" && $3 == "omarchy" && $4 == "--base-dir" && $5 == "$T3CODE_HOME" ]] || exit 1 +palette="$T3CODE_HOME/userdata/themes/omarchy.json" +if [[ ! -f $palette ]] || ! jq -e '.name == "Omarchy" and (.accent | test("^#[0-9a-fA-F]{6}$"))' "$palette" >/dev/null; then + echo "No published Omarchy theme" + exit 1 +fi +if [[ ${OMARCHY_TEST_T3_FAIL:-0} == "1" ]]; then + echo "T3 theme selection failed" + exit 1 +fi +echo "T3 selected Omarchy" +SH + +cat >"$mock_bin/setsid" <<'SH' +#!/bin/bash +exit 0 +SH +chmod +x "$mock_bin"/* + +run_installer() { + HOME="$test_home" T3CODE_HOME="$test_tmp/custom t3 home" \ + OMARCHY_PATH="$ROOT" PATH="$mock_bin:$ROOT/bin:$PATH" \ + OMARCHY_THEME_HEADLESS=1 XDG_RUNTIME_DIR="$test_tmp" \ + "$ROOT/bin/omarchy-install-ai-t3-code" >"$test_tmp/output" 2>&1 +} + +state="$test_home/.local/state/omarchy/current" +palette="$test_tmp/custom t3 home/userdata/themes/omarchy.json" +settings="$test_tmp/custom t3 home/userdata/settings.json" +mkdir -p "$state/theme" +echo 'tokyo-night' >"$state/theme.name" +cp "$ROOT/themes/tokyo-night/colors.toml" "$state/theme/colors.toml" + +run_installer || fail "installing after an update succeeds" "$(cat "$test_tmp/output")" +[[ -f $state/theme/t3code.json && -f $palette ]] || fail "the missing palette is rendered and published" +cmp -s "$state/theme/t3code.json" "$palette" || fail "T3 receives the current theme's palette" +grep -q 'T3 selected Omarchy' "$test_tmp/output" || fail "the published palette is selected" +grep -q 'Opening T3 Code' "$test_tmp/output" || fail "the themed app opens" +[[ ! -e $test_home/.t3 ]] || fail "the custom T3 home is respected" +pass "an update with no staged T3 palette renders, publishes and selects it before launch" + +# Preserve an already rendered palette, including a user-supplied color. +jq '.accent = "#abcdef"' "$state/theme/t3code.json" >"$test_tmp/palette.json" +cp "$test_tmp/palette.json" "$state/theme/t3code.json" +run_installer || fail "installing with an existing palette succeeds" "$(cat "$test_tmp/output")" +cmp -s "$test_tmp/palette.json" "$palette" || fail "an existing palette is published without re-staging" +pass "an existing palette is used without refreshing the current theme" + +printf '{"defaultTheme":"dark","keep":"existing settings"}\n' >"$settings" +cp "$settings" "$test_tmp/settings-before.json" +if OMARCHY_TEST_T3_FAIL=1 run_installer; then + fail "a failed T3 theme selection fails the installer" +fi +grep -q 'T3 theme selection failed' "$test_tmp/output" || fail "the CLI failure is visible" +if grep -Eq 'Opening T3 Code|T3 Code has been installed' "$test_tmp/output"; then + fail "a failed selection does not report successful setup" +fi +cmp -s "$test_tmp/settings-before.json" "$settings" || fail "a failed selection does not rewrite settings" +pass "a CLI failure remains visible and leaves the existing settings intact" diff --git a/test/shell.d/theme-staging-test.sh b/test/shell.d/theme-staging-test.sh index 9ec3b0f9..1fc7a4a5 100755 --- a/test/shell.d/theme-staging-test.sh +++ b/test/shell.d/theme-staging-test.sh @@ -207,7 +207,7 @@ pass "a theme name cannot climb out of the theme directories" # generates. Every generated theme file is either denied to an installed theme or # recorded here as carrying colour, so a new template fails until it is placed. denied=(alacritty.toml foot.ini ghostty.conf kitty.conf gum_env.lua hyprland.lua neovim.lua vscode.json) -colour_only=(btop.theme chromium.theme claude.json helix.toml hermes.yaml hyprland-preview-share-picker.css keyboard.rgb obsidian.css pi.json shell.toml vscode-theme.json) +colour_only=(btop.theme chromium.theme claude.json helix.toml hermes.yaml hyprland-preview-share-picker.css keyboard.rgb obsidian.css pi.json shell.toml t3code.json vscode-theme.json) for tpl in "$ROOT"/default/themed/*.tpl; do generated=$(basename "$tpl" .tpl)