oc-bg-color-sync 0.0.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -14
- package/index.ts +67 -47
- package/package.json +8 -10
package/README.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
Synchronize the terminal's native background color with OpenTUI's renderer background color. Fixes mismatched border/gutter colors in terminals that draw padding outside the application viewport.
|
|
4
4
|
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Requires OpenCode 2.x. Add the plugin to your OpenCode terminal client config (`~/.config/opencode/cli.json`):
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"plugins": ["oc-bg-color-sync"]
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### OR
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
opencode plugin add oc-bg-color-sync
|
|
19
|
+
```
|
|
20
|
+
|
|
5
21
|
## Why
|
|
6
22
|
|
|
7
23
|
OpenTUI renders its own background, but the terminal emulator may show its own default color in gutters, borders, or padding areas around the app. This plugin emits **OSC 11** whenever the renderer background changes so the terminal stays in sync.
|
|
@@ -40,13 +56,18 @@ This plugin restores the OSC 11 sync as a userland workaround until upstream fin
|
|
|
40
56
|
|
|
41
57
|
## How it works
|
|
42
58
|
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
59
|
+
- Registers via `Plugin.define` from `@opencode/plugin/tui` (the OpenCode 2 TUI plugin API)
|
|
60
|
+
- Wraps `context.renderer.setBackgroundColor()`
|
|
61
|
+
- After the native call, parses the color with `parseColor` from `@opentui/core` and writes `OSC 11;rgb:rr/gg/bb\x07` to stdout
|
|
62
|
+
- On plugin dispose, writes `OSC 111\x07` to restore the terminal's default background
|
|
46
63
|
|
|
47
64
|
### Timing fix
|
|
48
65
|
|
|
49
|
-
|
|
66
|
+
The TUI's theme provider calls `setBackgroundColor()` during its initial render **before** plugins load. If we only patched the method, the terminal would stay out of sync until the theme changed again. The plugin therefore reads `context.theme.background.base` after patching and calls the patched method once to force an immediate sync.
|
|
67
|
+
|
|
68
|
+
### Hot-reload safety
|
|
69
|
+
|
|
70
|
+
OpenCode 2 reloads plugins on config changes without restarting the TUI. The cleanup function restores the original `setBackgroundColor` (verified by identity, so it never stomps a newer plugin generation or another plugin's patch) before emitting `OSC 111`, so each reload unwinds cleanly instead of stacking wrappers.
|
|
50
71
|
|
|
51
72
|
### OSC sequence reference
|
|
52
73
|
|
|
@@ -57,16 +78,6 @@ This plugin restores the OSC 11 sync as a userland workaround until upstream fin
|
|
|
57
78
|
|
|
58
79
|
See [VT100.net OSC 11 docs](https://vtdn.dev/docs/osc/osc11) and [OSC 110-112](https://vtdn.dev/docs/osc/osc110-112) for more.
|
|
59
80
|
|
|
60
|
-
## Install
|
|
61
|
-
|
|
62
|
-
Add the plugin to your OpenCode config (`tui.json` or equivalent):
|
|
63
|
-
|
|
64
|
-
```json
|
|
65
|
-
{
|
|
66
|
-
"plugin": ["oc-bg-color-sync"]
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
81
|
## Debugging
|
|
71
82
|
|
|
72
83
|
To verify the plugin is emitting OSC sequences, run OpenCode in a terminal that logs escape sequences, or sniff stdout with `script`:
|
|
@@ -86,6 +97,7 @@ You should see an `OSC 11` on startup and an `OSC 111` on exit.
|
|
|
86
97
|
|
|
87
98
|
## Requirements
|
|
88
99
|
|
|
100
|
+
- OpenCode 2.x
|
|
89
101
|
- A terminal emulator that supports OSC 11/111 (most modern terminals do; Ghostty has quirks with theme detection after OSC 11/111 usage)
|
|
90
102
|
|
|
91
103
|
## License
|
package/index.ts
CHANGED
|
@@ -1,51 +1,71 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { writeSync } from "fs"
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
import { Plugin } from "@opencode/plugin/tui"
|
|
2
|
+
import { parseColor, type ColorInput } from "@opentui/core"
|
|
3
|
+
import { writeSync } from "fs"
|
|
4
|
+
|
|
5
|
+
const hexByte = (n: number) =>
|
|
6
|
+
Math.round(n * 255)
|
|
7
|
+
.toString(16)
|
|
8
|
+
.padStart(2, "0")
|
|
9
|
+
|
|
10
|
+
const writeOsc = (sequence: string) => {
|
|
11
|
+
// writeSync(1, …) bypasses Node.js stream buffering so the sequence is
|
|
12
|
+
// flushed to the kernel immediately, even while the process is shutting
|
|
13
|
+
// down. process.stdout.write can leave bytes in the stream buffer that
|
|
14
|
+
// never get drained once the event loop exits.
|
|
15
|
+
try {
|
|
16
|
+
writeSync(1, sequence)
|
|
17
|
+
} catch {
|
|
18
|
+
process.stdout.write(sequence)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default Plugin.define({
|
|
23
|
+
id: "bg-sync",
|
|
24
|
+
setup(context) {
|
|
25
|
+
const renderer = context.renderer
|
|
26
|
+
|
|
27
|
+
// Capture whatever is installed right now: normally the prototype
|
|
28
|
+
// method, but possibly another plugin's wrapper or an earlier
|
|
29
|
+
// hot-reload generation of this plugin.
|
|
30
|
+
const descriptor = Object.getOwnPropertyDescriptor(renderer, "setBackgroundColor")
|
|
31
|
+
const previous = renderer.setBackgroundColor.bind(renderer)
|
|
32
|
+
|
|
33
|
+
const patched = (color: ColorInput) => {
|
|
34
|
+
previous(color)
|
|
35
|
+
const rgba = parseColor(color)
|
|
36
|
+
// "transparent" parses to a = 0; leave the terminal's default alone
|
|
37
|
+
// instead of forcing an opaque background.
|
|
38
|
+
if (rgba.a > 0) {
|
|
39
|
+
writeOsc(`\x1b]11;rgb:${hexByte(rgba.r)}/${hexByte(rgba.g)}/${hexByte(rgba.b)}\x07`)
|
|
23
40
|
}
|
|
24
41
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// ThemeProvider calls setBackgroundColor during initial render, which happens
|
|
28
|
-
// *before* plugins are loaded. After patching we must force a re-sync with the
|
|
29
|
-
// current theme background so the terminal matches on startup.
|
|
30
|
-
renderer.setBackgroundColor(api.theme.current.background);
|
|
31
|
-
|
|
32
|
-
api.lifecycle.onDispose(() => {
|
|
33
|
-
// Use writeSync(1, …) instead of process.stdout.write so the OSC 111
|
|
34
|
-
// sequence bypasses Node.js stream buffering and is flushed to the kernel
|
|
35
|
-
// before the process exits. renderer.resetTerminalBgColor() uses
|
|
36
|
-
// process.stdout.write, which can leave bytes in the stream buffer that
|
|
37
|
-
// never get drained when the event loop shuts down.
|
|
38
|
-
try {
|
|
39
|
-
writeSync(1, "\x1b]111\x07");
|
|
40
|
-
} catch {
|
|
41
|
-
renderer.resetTerminalBgColor();
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
};
|
|
42
|
+
renderer.setBackgroundColor = patched
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
// The theme provider calls setBackgroundColor during its initial render,
|
|
45
|
+
// which happens before plugins load. After patching, re-apply the
|
|
46
|
+
// current theme background once so the terminal matches on startup.
|
|
47
|
+
renderer.setBackgroundColor(context.theme.background.base)
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
// Only unwind our own patch. The host tears the old generation down
|
|
51
|
+
// before setting up a new one, but if a newer generation (or another
|
|
52
|
+
// plugin) has already replaced the method, restoring here would
|
|
53
|
+
// silently break it — and resetting the terminal background would
|
|
54
|
+
// clobber the state that replacement just installed.
|
|
55
|
+
if (renderer.setBackgroundColor !== patched) return
|
|
56
|
+
|
|
57
|
+
if (descriptor) Object.defineProperty(renderer, "setBackgroundColor", descriptor)
|
|
58
|
+
else delete (renderer as { setBackgroundColor?: (color: ColorInput) => void }).setBackgroundColor
|
|
50
59
|
|
|
51
|
-
|
|
60
|
+
// OSC 111 restores the terminal's default background. Prefer writeSync
|
|
61
|
+
// so the bytes are flushed before the process exits; the renderer's
|
|
62
|
+
// resetTerminalBgColor() writes through a path that may not drain in
|
|
63
|
+
// time.
|
|
64
|
+
try {
|
|
65
|
+
writeSync(1, "\x1b]111\x07")
|
|
66
|
+
} catch {
|
|
67
|
+
renderer.resetTerminalBgColor()
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
})
|
package/package.json
CHANGED
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oc-bg-color-sync",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
|
-
"./tui":
|
|
7
|
-
"import": "./index.ts"
|
|
8
|
-
}
|
|
6
|
+
"./tui": "./index.ts"
|
|
9
7
|
},
|
|
10
8
|
"files": [
|
|
11
9
|
"index.ts"
|
|
12
10
|
],
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@opencode/plugin": "latest"
|
|
13
|
+
},
|
|
13
14
|
"peerDependencies": {
|
|
14
|
-
"@
|
|
15
|
-
"@opentui/core": "*",
|
|
16
|
-
"@opentui/solid": "*",
|
|
17
|
-
"solid-js": "*"
|
|
15
|
+
"@opentui/core": ">=0.6.6"
|
|
18
16
|
},
|
|
19
17
|
"devDependencies": {
|
|
20
|
-
"
|
|
21
|
-
"
|
|
18
|
+
"@types/bun": "latest",
|
|
19
|
+
"typescript": "^5"
|
|
22
20
|
}
|
|
23
21
|
}
|