Repository navigation
Conversation
There was a problem hiding this comment.
Pull request overview
Adds live OpenCode theme synchronization without restarting sessions or interrupting agents.
Changes:
- Adds an OpenCode theme template, synchronization command, and hot-reload plugin.
- Activates the integration for new installs and migrates existing installations.
- Replaces restart-based theme updates and adds CLI/template coverage.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Reviewed changes
Copilot reviewed 6 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
bin/omarchy-theme-set |
Uses theme synchronization instead of restarting OpenCode. |
bin/omarchy-theme-set-opencode |
Synchronizes and optionally activates the OpenCode theme integration. |
config/opencode/tui-plugins/omarchy-theme.ts |
Watches and hot-swaps generated themes. |
default/themed/opencode.json.tpl |
Defines the generated OpenCode palette. |
install/user/theme.sh |
Activates integration during user setup. |
migrations/1787871792.sh |
Migrates existing OpenCode installations. |
test/cli |
Tests generation, synchronization, and activation. |
test/shell.d/theme-staging-test.sh |
Classifies the generated theme as color-only. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 9 changed files in this pull request and generated 3 comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
config/opencode/tui-plugins/omarchy-theme.ts:40
- The shell command treats empty
OPENCODE_CONFIG_DIR/XDG_CONFIG_HOMEvalues as unset via:-, but the plugin's nullish checks accept empty strings. In that environment the command writes under~/.config/opencode, while the plugin watches a relativeopencode/themesdirectory, so live retinting never occurs. Use truthy fallback here to keep both paths identical.
const config =
process.env.OPENCODE_CONFIG_DIR ??
path.join(process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"), "opencode")
|
Addressed the remaining Copilot finding from the second review round: the TUI plugin's |
ec8ee2b to
67e763e
Compare
|
Independent verification of this PR, from arriving at the same design separately while debugging opencode lagging a theme behind under a terminal multiplexer. Tested on omarchy 4.0.3 (this branch is based on 4.0.0.alpha), Arch, foot 1.28.0 + opencode 1.18.29. To avoid patching packaged files I applied the two portable pieces through the supported override paths — What verified clean
Evidence for the plugin approach specifically The design argument for the TUI plugin over a plain file sync holds up under measurement. The consequence is that a file-sync-only integration cannot retint a session that was already running when the integration was installed — it needs a restart to pick up Context that may help triage: there are currently three open PRs covering this ground — this one, #8264, and #11402 — and none has a review. This is the only one of the three that addresses live retint, install/migration activation, and the Not verified: live retint inside a long-running session with the plugin loaded. I could confirm the plugin file installs and its import resolves, but not observe an in-process swap, so that part of the claim is untested here rather than disputed. Separately, and not this PR's problem: the reason opencode was singled out in my case is that under a multiplexer whose PTYs are owned by a detached server, |
|
Thanks for the thorough independent verification — especially the To close the one open loop: live retint in a long-running session is covered. I verified it with an opencode TUI running under tmux across several consecutive palette swaps (rewriting Two notes on the current head, since the branch moved on from the revision you tested:
Agreed on the foot/multiplexer point too — pane-walking with On the three-PR overlap: this remains the only one of the three covering live retint plus activation, so they compose rather than conflict — happy to adjust if maintainers would rather converge on one approach. |
|
Follow-up pushed: the migration now handles staged themes that predate this PR. Every existing install has a staged theme from before Also merged |
f9686f8 to
10fe602
Compare
|
Rebased onto current quattro (2fbac0c). One conflict in the append-only colour_only list in theme-staging-test.sh, resolved as sorted union (keeps hermes.yaml / t3code.json, adds opencode.json). opencode-theme-migration-test.sh and theme-staging-test.sh pass. |
|
Follow-up fix: react to every themes-dir event in the OpenCode watcher (c3a41a2). Wallpaper/theme changes synced omarchy.json correctly, but running sessions never retinted. Root cause: the watcher only reacted to events named omarchy.json, and opencode runs on Bun, whose fs.watch does not reliably report the atomic rename that replaces omarchy.json -- only the temp-file write/rename precursors arrive (verified with an instrumented plugin in a test TUI: startup apply works, watch events for the replace never fire; Node reports them fine). The fix schedules the debounced apply on any directory event instead; the apply re-reads omarchy.json itself, so unrelated files changing here cost at most one cheap re-read. Verified live: a fresh TUI installs and selects the current palette on startup, and theme switches in both directions retint the running session within seconds (hash install, kv.json update, stale prune). Also added test/shell.d/opencode-theme-plugin-test.sh as a regression guard. test/cli passes 120/120. Side note: this also means wallpaperctl's watcher-nudge rewrite cannot work as intended on this Bun version -- tmp+replace is equally invisible there -- but the plugin-side fix covers it, since the nudge's temp file itself now triggers an apply. |
20e49ad to
b8a5e54
Compare
b8a5e54 to
bf5e17b
Compare
omarchy-theme-set sent SIGUSR2 to opencode after every theme change. That signal makes opencode reload its worker, which disposes all instances and interrupts any agent running in a session. OpenCode themes are plain JSON in ~/.config/opencode/themes, and a TUI plugin can hot-swap the live theme in-process. So this follows the Claude Code/Pi integration shape and drops the signal: * An opencode.json template renders from each theme's colors.toml, so every stock and community theme is covered. * omarchy-theme-set-opencode copies the result into ~/.config/opencode/themes/omarchy.json and replaces the restart in the theme's post commands. With --activate it also installs the shipped omarchy-theme TUI plugin and points tui.json at the theme. * The plugin watches the synced file and retints running sessions live; running agents are never touched. Selecting Omarchy in the /theme picker (or --activate) opts a session in; without that the sync is a no-op at runtime. * A migration wires the plugin into tui.json for existing installs, without switching their theme.
* Resolve the opencode config directory the way opencode does --
OPENCODE_CONFIG_DIR, then XDG_CONFIG_HOME, then ~/.config -- in the
sync script and the migration, so XDG installs are migrated into the
tree opencode actually reads.
* Stage the theme in a mkdtemp directory removed in finally: every
running session stages the same palette at the same deterministic
path, so one session's cleanup could pull the file out from under
another session's install.
* Create a plugin-only tui.json when the install has none, which is
most existing installs; omitting "theme" keeps their current choice.
* Restrict pruning to the generated omarchy-<8 hex>.json shape so a
user theme like omarchy-custom.json survives every apply.
* Skip theme files whose colors carry unresolved {{ }} template
tokens; resolveTheme() throws on them and would take the TUI down.
* Cover the migration with opencode-theme-migration-test.sh.
…rder * owned() now matches the same generated omarchy-<8 hex> shape as pruning, so picking a user theme named omarchy-something is inert and the next synced palette does not take over. * Applies are serialized on a promise chain: theme.install() awaits, so a newer file event could otherwise interleave with an apply still finishing and let a stale invocation set an older palette or prune the newer copy. Each apply re-reads the file, so the last event wins. * Register the plugin by appending, not unique: plugin order affects initialization, so the migration and --activate must not reorder the user's existing list. * The cli suite's skip-when-missing invocation now unsets XDG_CONFIG_HOME/OPENCODE_CONFIG_DIR; with XDG support the script would otherwise resolve the real config directory on machines where it is set.
…plugin
An empty-but-set OPENCODE_CONFIG_DIR or XDG_CONFIG_HOME previously made the
plugin watch a different tree than omarchy-theme-set-opencode writes to, so
live retinting never fired. Match the sync script's ${VAR:-} semantics and
document why the fallbacks are truthy.
Skip theme.install/prune unless this session already has Omarchy selected, so migrating the plugin in does not list hashed omarchy-* themes for users who never opted in. Treat empty OPENCODE_CONFIG_DIR as unset in the CLI test, and drop the stale "used by theme switching" note on the restart command.
Live retint is the point: the migration now selects the Omarchy theme, and the plugin treats "system" as follow-the-desktop (that path used to depend on SIGUSR2). Picking any other theme in OpenCode still opts out.
system is terminal-adaptive (ANSI / none) and never reads omarchy.json. Live retint stays on the omarchy theme; picking system is an opt-out.
Every existing install has a staged theme from before opencode.json.tpl shipped, so the migration's sync would find no source file and leave no themes/omarchy.json behind -- while tui.json already points at it. Re-stage once when the recorded theme still exists (cf. 1787481315); a removed theme is left alone so the migration cannot fail the update.
opencode runs on Bun, whose fs.watch does not reliably report the atomic rename that replaces omarchy.json (only the temp-file precursors arrive), so running sessions never retinted on wallpaper/theme changes. Schedule the debounced apply on any directory event instead; it re-reads omarchy.json itself, so unrelated files cost one cheap re-read.
bf5e17b to
5455a56
Compare
|
Superseded by #15350, which targets OpenCode 2 explicitly. The original SIGUSR2 session-disposal bug appears fixed upstream in 2.0.28; the replacement retains explicit Omarchy palette synchronization using the new TUI API and cli.json, without the legacy 1.x adapter. The replacement is open and mergeable, and has been verified in an integration build with all other selected PRs while excluding this PR; focused tests pass on that combined tree. Closing this only after the replacement was prepared and verified keeps the theme integration covered. |
Sync Omarchy's generated palette into OpenCode without restarting its server or interrupting agents. The template and sync command produce
themes/omarchy.json; startup activation selects it, and choosing another theme opts out of its visual changes.OpenCode 1.x uses the existing in-process TUI theme adapter. OpenCode 2.0.28 no longer exposes that theme-management API, so the plugin now also exports a
setupadapter that watches atomic palette replacements and emitsSIGUSR2inside its own TUI process. OpenCode 2 handles this as palette/custom-theme refresh, not server-instance reload. No OS signal is sent to the shared server.For OpenCode 2, the sync command registers a directory-based TUI entrypoint in
cli.jsonand removes the migrated legacy file registration. Ordinary syncs repair registration while preserving the user's theme selection and other plugins, including Herdr. Explicit--activateselects Omarchy while preserving the configured light/dark mode. The legacytui.jsonactivation remains for 1.x.The original session-disposal bug motivating this PR appears fixed upstream in OpenCode 2.0.28; the remaining value is explicit Omarchy palette synchronization and compatibility with both plugin generations. The manual restart command remains available but theme changes use palette sync instead.
Validation: plugin watcher and migration tests pass. New OpenCode 2 coverage checks actual adapter import, atomic replacements triggering one debounced local refresh, cleanup, activation, custom-theme opt-out and Herdr preservation. A separate 2.0.28 TUI was opened without starting agent work; changing and restoring a temporary palette redrew it. The selected-PR integration rebuild passes. No live in-flight-agent interruption test is claimed.