@brimveyn/aimux 1.19.1 → 1.19.2
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/package.json
CHANGED
|
@@ -1,32 +1,12 @@
|
|
|
1
1
|
import type { ModeId } from '@brimveyn/aimux-config'
|
|
2
2
|
|
|
3
|
-
import { type BoxRenderable, type OptimizedBuffer, RGBA } from '@opentui/core'
|
|
4
|
-
|
|
5
3
|
import { describeBindings } from '../../../../input/keymap/describe-bindings'
|
|
6
4
|
import { useKeymap } from '../../../keymap-context'
|
|
7
5
|
import { useTheme, useTransparent } from '../../../theme'
|
|
6
|
+
import { fillBoxInterior } from '../../../transparent-fill'
|
|
8
7
|
|
|
9
8
|
const KEYS_COLUMN_WIDTH = 12
|
|
10
9
|
|
|
11
|
-
const TRANSPARENT_RGBA = RGBA.fromValues(0, 0, 0, 0)
|
|
12
|
-
|
|
13
|
-
// Same trick as modal-shell: when our bg is transparent, BoxRenderable skips
|
|
14
|
-
// its fill so the chrome chars underneath leak through. setCell bypasses alpha
|
|
15
|
-
// blending and char-preservation, so we can wipe every interior cell to
|
|
16
|
-
// (' ', TRANSPARENT_RGBA, TRANSPARENT_RGBA). No border on this overlay, so
|
|
17
|
-
// fill the full bounds (no 1-cell inset).
|
|
18
|
-
function fillBoxInteriorWithSpaces(this: BoxRenderable, buffer: OptimizedBuffer): void {
|
|
19
|
-
const x0 = this.screenX
|
|
20
|
-
const y0 = this.screenY
|
|
21
|
-
const endX = x0 + this.width
|
|
22
|
-
const endY = y0 + this.height
|
|
23
|
-
for (let y = y0; y < endY; y++) {
|
|
24
|
-
for (let x = x0; x < endX; x++) {
|
|
25
|
-
buffer.setCell(x, y, ' ', TRANSPARENT_RGBA, TRANSPARENT_RGBA)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
10
|
interface ModalKeybindsOverlayProps {
|
|
31
11
|
modeId: ModeId
|
|
32
12
|
limit?: number
|
|
@@ -52,7 +32,7 @@ export function ModalKeybindsOverlay({ limit, modeId }: ModalKeybindsOverlayProp
|
|
|
52
32
|
paddingRight={2}
|
|
53
33
|
paddingTop={1}
|
|
54
34
|
paddingBottom={1}
|
|
55
|
-
renderAfter={transparent ?
|
|
35
|
+
renderAfter={transparent ? fillBoxInterior : undefined}
|
|
56
36
|
>
|
|
57
37
|
{entries.map((binding) => (
|
|
58
38
|
<box key={binding.description ?? binding.keys} flexDirection="row">
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { ModeId } from '@brimveyn/aimux-config'
|
|
2
2
|
import type { ReactNode } from 'react'
|
|
3
3
|
|
|
4
|
-
import { type BoxRenderable, type OptimizedBuffer, RGBA } from '@opentui/core'
|
|
5
|
-
|
|
6
4
|
import { useTheme, useTransparent } from '../../../theme'
|
|
5
|
+
import { fillBorderedBoxInterior } from '../../../transparent-fill'
|
|
7
6
|
import { ModalKeybindsOverlay } from './modal-keybinds-overlay'
|
|
8
7
|
|
|
9
8
|
interface ModalShellProps {
|
|
@@ -16,38 +15,6 @@ interface ModalShellProps {
|
|
|
16
15
|
width: number | `${number}%`
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
const TRANSPARENT_RGBA = RGBA.fromValues(0, 0, 0, 0)
|
|
20
|
-
|
|
21
|
-
// opentui's BoxRenderable only paints a fill when `backgroundColor.a > 0`, so
|
|
22
|
-
// a fully transparent modal bg leaks the chrome characters underneath. The
|
|
23
|
-
// `renderAfter` hook runs after the box paints itself but before its children
|
|
24
|
-
// render, so we manually overwrite every interior cell with a blank space.
|
|
25
|
-
//
|
|
26
|
-
// Two gotchas we hit by reading the zig source (packages/core/src/zig/buffer.zig):
|
|
27
|
-
// 1. `setCellWithAlphaBlending` early-returns via `isFullyTransparent` when
|
|
28
|
-
// both fg and bg alpha are 0 — nothing gets written.
|
|
29
|
-
// 2. Its `blendCells` deliberately preserves the destination char when the
|
|
30
|
-
// overlay char is `DEFAULT_SPACE_CHAR` (codepoint 32) so that drawing a
|
|
31
|
-
// space on top of existing text does not erase it.
|
|
32
|
-
//
|
|
33
|
-
// `setCell` (→ `bufferSetCell` → zig `buffer.set`) bypasses both: it writes
|
|
34
|
-
// the cell unconditionally, no alpha check, no blending, no char preservation.
|
|
35
|
-
function fillModalInteriorWithSpaces(this: BoxRenderable, buffer: OptimizedBuffer): void {
|
|
36
|
-
const x0 = this.screenX
|
|
37
|
-
const y0 = this.screenY
|
|
38
|
-
const w = this.width
|
|
39
|
-
const h = this.height
|
|
40
|
-
const startX = x0 + 1
|
|
41
|
-
const startY = y0 + 1
|
|
42
|
-
const endX = x0 + w - 1
|
|
43
|
-
const endY = y0 + h - 1
|
|
44
|
-
for (let y = startY; y < endY; y++) {
|
|
45
|
-
for (let x = startX; x < endX; x++) {
|
|
46
|
-
buffer.setCell(x, y, ' ', TRANSPARENT_RGBA, TRANSPARENT_RGBA)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
18
|
export function ModalShell({
|
|
52
19
|
children,
|
|
53
20
|
footer,
|
|
@@ -76,7 +43,7 @@ export function ModalShell({
|
|
|
76
43
|
backgroundColor={bg}
|
|
77
44
|
padding={1}
|
|
78
45
|
width={width}
|
|
79
|
-
renderAfter={transparent ?
|
|
46
|
+
renderAfter={transparent ? fillBorderedBoxInterior : undefined}
|
|
80
47
|
>
|
|
81
48
|
<box width="100%" flexDirection="column" gap={listGap}>
|
|
82
49
|
<box flexDirection="column">
|
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
type ContextMenuState,
|
|
10
10
|
subscribeContextMenu,
|
|
11
11
|
} from '../../../context-menu/controller'
|
|
12
|
-
import { useTheme } from '../../../theme'
|
|
12
|
+
import { useTheme, useTransparent } from '../../../theme'
|
|
13
|
+
import { fillBorderedBoxInterior } from '../../../transparent-fill'
|
|
13
14
|
|
|
14
15
|
const MenuItem = memo(function MenuItem({
|
|
15
16
|
active,
|
|
@@ -59,6 +60,7 @@ export function ContextMenuOverlay() {
|
|
|
59
60
|
const [menu, setMenu] = useState<ContextMenuState | null>(null)
|
|
60
61
|
const [selected, setSelected] = useState(0)
|
|
61
62
|
const t = useTheme()
|
|
63
|
+
const transparent = useTransparent()
|
|
62
64
|
const terminalCols = useAppStore((s) => s.layout.terminalCols)
|
|
63
65
|
const terminalRows = useAppStore((s) => s.layout.terminalRows)
|
|
64
66
|
|
|
@@ -131,7 +133,8 @@ export function ContextMenuOverlay() {
|
|
|
131
133
|
flexDirection="column"
|
|
132
134
|
border
|
|
133
135
|
borderColor={t.border}
|
|
134
|
-
backgroundColor={t.backgroundPanel}
|
|
136
|
+
backgroundColor={transparent ? 'transparent' : t.backgroundPanel}
|
|
137
|
+
renderAfter={transparent ? fillBorderedBoxInterior : undefined}
|
|
135
138
|
onMouseDown={handleMenuMouseDown}
|
|
136
139
|
>
|
|
137
140
|
{menu.items.map(([label, onSelect], index) => (
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type BoxRenderable, type OptimizedBuffer, RGBA } from '@opentui/core'
|
|
2
|
+
|
|
3
|
+
export const TRANSPARENT_RGBA = RGBA.fromValues(0, 0, 0, 0)
|
|
4
|
+
|
|
5
|
+
// opentui's BoxRenderable only paints a fill when `backgroundColor.a > 0`, so
|
|
6
|
+
// a fully transparent bg leaks the chrome characters underneath. These helpers
|
|
7
|
+
// run from a box's `renderAfter` hook (after the box paints itself, before its
|
|
8
|
+
// children render) and manually overwrite every interior cell with a blank
|
|
9
|
+
// space so the region reads as "emptied" instead of leaking.
|
|
10
|
+
//
|
|
11
|
+
// Two gotchas we hit by reading the zig source (packages/core/src/zig/buffer.zig):
|
|
12
|
+
// 1. `setCellWithAlphaBlending` early-returns via `isFullyTransparent` when
|
|
13
|
+
// both fg and bg alpha are 0 — nothing gets written.
|
|
14
|
+
// 2. Its `blendCells` deliberately preserves the destination char when the
|
|
15
|
+
// overlay char is `DEFAULT_SPACE_CHAR` (codepoint 32) so that drawing a
|
|
16
|
+
// space on top of existing text does not erase it.
|
|
17
|
+
//
|
|
18
|
+
// `setCell` (→ `bufferSetCell` → zig `buffer.set`) bypasses both: it writes
|
|
19
|
+
// the cell unconditionally, no alpha check, no blending, no char preservation.
|
|
20
|
+
|
|
21
|
+
// Bordered boxes: inset by 1 cell so the painted border is preserved.
|
|
22
|
+
export function fillBorderedBoxInterior(this: BoxRenderable, buffer: OptimizedBuffer): void {
|
|
23
|
+
const x0 = this.screenX
|
|
24
|
+
const y0 = this.screenY
|
|
25
|
+
const w = this.width
|
|
26
|
+
const h = this.height
|
|
27
|
+
const startX = x0 + 1
|
|
28
|
+
const startY = y0 + 1
|
|
29
|
+
const endX = x0 + w - 1
|
|
30
|
+
const endY = y0 + h - 1
|
|
31
|
+
for (let y = startY; y < endY; y++) {
|
|
32
|
+
for (let x = startX; x < endX; x++) {
|
|
33
|
+
buffer.setCell(x, y, ' ', TRANSPARENT_RGBA, TRANSPARENT_RGBA)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Borderless boxes: fill the full bounds (no inset).
|
|
39
|
+
export function fillBoxInterior(this: BoxRenderable, buffer: OptimizedBuffer): void {
|
|
40
|
+
const x0 = this.screenX
|
|
41
|
+
const y0 = this.screenY
|
|
42
|
+
const endX = x0 + this.width
|
|
43
|
+
const endY = y0 + this.height
|
|
44
|
+
for (let y = y0; y < endY; y++) {
|
|
45
|
+
for (let x = x0; x < endX; x++) {
|
|
46
|
+
buffer.setCell(x, y, ' ', TRANSPARENT_RGBA, TRANSPARENT_RGBA)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|