Theme scrollbars + global broadcast toggle
Two small QoL additions:
- styles.css: WebKit pseudo-element styling for .xterm-viewport
scrollbars (8px wide, dark thumb #2a2a2a on transparent track,
hover lighten). Matches the rest of the dark theme so the right
edge of each terminal stops looking like default OS chrome.
- tree.ts: setAllBroadcast(root, on) helper that flips every leaf's
broadcast flag to the given value, preserving object identity
where nothing changed.
- App.tsx: titlebar 📡 button showing global broadcast state
("all off" / "all on" / "N/M"). Click toggles every pane between
all-broadcasting and all-off. Orange when any panes are
broadcasting; darker orange when partial.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
774b8633dc
commit
369ec8e2fd
4 changed files with 86 additions and 0 deletions
|
|
@ -47,6 +47,15 @@
|
|||
color: #cce6ff;
|
||||
border-color: #2a5a8c;
|
||||
}
|
||||
.palette-btn.bcast-all.on {
|
||||
background: #4a3010;
|
||||
color: #f0c060;
|
||||
border-color: #c98a1f;
|
||||
}
|
||||
.palette-btn.bcast-all.on.partial {
|
||||
background: #2a2010;
|
||||
color: #c98a1f;
|
||||
}
|
||||
.preset-btn {
|
||||
min-width: 28px;
|
||||
text-align: center;
|
||||
|
|
|
|||
38
src/App.tsx
38
src/App.tsx
|
|
@ -21,6 +21,7 @@ import {
|
|||
changeDistro,
|
||||
changeLabel,
|
||||
toggleBroadcast as toggleBroadcastInTree,
|
||||
setAllBroadcast,
|
||||
serialize,
|
||||
deserialize,
|
||||
presetSingle,
|
||||
|
|
@ -282,6 +283,29 @@ export default function App() {
|
|||
[paletteOpen, tree],
|
||||
);
|
||||
|
||||
// ---- global broadcast state (derived from tree) -------------------------
|
||||
const broadcastStats = useMemo(() => {
|
||||
let on = 0;
|
||||
let total = 0;
|
||||
for (const leaf of walkLeaves(tree)) {
|
||||
total++;
|
||||
if (leaf.broadcast) on++;
|
||||
}
|
||||
return { on, total };
|
||||
}, [tree]);
|
||||
|
||||
const toggleBroadcastAll = useCallback(() => {
|
||||
// If any pane is broadcasting, turn them all off. Otherwise turn them all on.
|
||||
setTree((t) => setAllBroadcast(t, broadcastStats.on === 0));
|
||||
}, [broadcastStats.on]);
|
||||
|
||||
const broadcastBtnLabel =
|
||||
broadcastStats.on === 0
|
||||
? "📡 all off"
|
||||
: broadcastStats.on === broadcastStats.total
|
||||
? "📡 all on"
|
||||
: `📡 ${broadcastStats.on}/${broadcastStats.total}`;
|
||||
|
||||
const onPalettePick = useCallback((leafId: string) => {
|
||||
setActiveLeafId(leafId);
|
||||
setPaletteOpen(false);
|
||||
|
|
@ -321,6 +345,20 @@ export default function App() {
|
|||
<button className="preset-btn" title="2 × 2 grid" onClick={() => applyPreset(presetTwoByTwo)}>2×2</button>
|
||||
</span>
|
||||
|
||||
<button
|
||||
className={`palette-btn bcast-all${broadcastStats.on > 0 ? " on" : ""}${broadcastStats.on > 0 && broadcastStats.on < broadcastStats.total ? " partial" : ""}`}
|
||||
onClick={toggleBroadcastAll}
|
||||
title={
|
||||
broadcastStats.on === 0
|
||||
? "Click to broadcast input to ALL panes"
|
||||
: broadcastStats.on === broadcastStats.total
|
||||
? "All panes broadcasting — click to disable"
|
||||
: `${broadcastStats.on} of ${broadcastStats.total} panes broadcasting — click to disable all`
|
||||
}
|
||||
>
|
||||
{broadcastBtnLabel}
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="palette-btn"
|
||||
onClick={() => setPaletteOpen(true)}
|
||||
|
|
|
|||
|
|
@ -198,6 +198,19 @@ export function toggleBroadcast(root: TreeNode, leafId: NodeId): TreeNode {
|
|||
});
|
||||
}
|
||||
|
||||
/** Force every leaf's broadcast flag to `on`. Returns the same root reference
|
||||
* when nothing actually changed, so callers can skip a state update if so. */
|
||||
export function setAllBroadcast(root: TreeNode, on: boolean): TreeNode {
|
||||
if (root.kind === "leaf") {
|
||||
if (!!root.broadcast === on) return root;
|
||||
return { ...root, broadcast: on };
|
||||
}
|
||||
const a = setAllBroadcast(root.a, on);
|
||||
const b = setAllBroadcast(root.b, on);
|
||||
if (a === root.a && b === root.b) return root;
|
||||
return { ...root, a, b };
|
||||
}
|
||||
|
||||
export function serialize(root: TreeNode): string {
|
||||
return JSON.stringify(root);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,3 +37,29 @@ body {
|
|||
|
||||
.xterm { height: 100%; }
|
||||
.xterm-viewport { background: #0c0c0c !important; }
|
||||
|
||||
/* Themed scrollbars — Chromium pseudo-elements (WebView2 supports these). */
|
||||
.xterm-viewport::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
.xterm-viewport::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
.xterm-viewport::-webkit-scrollbar-thumb {
|
||||
background: #2a2a2a;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #1a1a1a;
|
||||
}
|
||||
.xterm-viewport::-webkit-scrollbar-thumb:hover {
|
||||
background: #3a3a3a;
|
||||
}
|
||||
.xterm-viewport::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
/* Firefox fallback (and the new spec) — not strictly needed in WebView2
|
||||
but free-and-correct. */
|
||||
.xterm-viewport {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #2a2a2a transparent;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue