Compare commits

...
Author SHA1 Message Date
Miguel PalhasandClaude Opus 4.6 563a35d2ec fix(maestro): restore sidebar fill, pane title, and focus dimming after React port
ci / web (push) Failing after 6s
ci / rust (push) Failing after 48s
ci / flake (push) Has been cancelled
The React port replaced CSS class names with Tailwind utilities, breaking
three visual contracts:

- SessionCard used inline bg-* classes instead of .is-active/.is-open/
  .is-previewing that the styles.css cascade targets.
- PaneTitle used className "at-title" instead of "attach-title", so font,
  overflow, and child layout rules never applied.
- MultiView's Pane never applied .is-focused or .is-preview, so every
  pane matched :not(.is-focused) and was dimmed to 0.45 opacity.

Closes #256

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-09-10 21:51:53 +01:00
4 changed files with 19 additions and 18 deletions
+1 -1
View File
@@ -105,7 +105,7 @@ describe("MultiView", () => {
expect(screen.getByText("Tab 1 · 1")).toBeTruthy(); expect(screen.getByText("Tab 1 · 1")).toBeTruthy();
const pane = document.querySelector('[data-session-id="s9"]'); const pane = document.querySelector('[data-session-id="s9"]');
expect(pane).toBeTruthy(); expect(pane).toBeTruthy();
expect(pane!.querySelector(".bg-preview-tint")).toBeTruthy(); expect(pane!.classList.contains("is-preview")).toBe(true);
expect(screen.getByText("preview")).toBeTruthy(); expect(screen.getByText("preview")).toBeTruthy();
}); });
+4 -6
View File
@@ -188,8 +188,9 @@ function Pane({
return ( return (
<div <div
className={ className={
"mv-pane flex h-full min-h-0 min-w-0 flex-col border " + "mv-pane flex h-full min-h-0 min-w-0 flex-col" +
(isFocused && !isPreview ? "border-accent" : "border-border-strong") (isFocused ? " is-focused" : "") +
(isPreview ? " is-preview" : "")
} }
data-leaf-id={leaf.id} data-leaf-id={leaf.id}
data-session-id={leaf.sessionId ?? undefined} data-session-id={leaf.sessionId ?? undefined}
@@ -201,10 +202,7 @@ function Pane({
}} }}
> >
<div <div
className={ className="mv-pane-header attach-header flex items-center justify-between gap-sp-2 border-b border-border-strong px-sp-2 py-sp-1 text-fs-12"
"mv-pane-header flex items-center justify-between gap-sp-2 border-b border-border-strong px-sp-2 py-sp-1 text-fs-12 " +
(isPreview ? "bg-preview-tint text-preview" : "bg-surface")
}
> >
{leaf.sessionId ? ( {leaf.sessionId ? (
<PaneTitle sessionId={leaf.sessionId} session={session} /> <PaneTitle sessionId={leaf.sessionId} session={session} />
+2 -2
View File
@@ -15,7 +15,7 @@ export interface PaneTitleProps {
export function PaneTitle({ sessionId, session, className }: PaneTitleProps) { export function PaneTitle({ sessionId, session, className }: PaneTitleProps) {
if (!session) { if (!session) {
return ( return (
<span className={className ?? "at-title"} title={sessionId}> <span className={className ?? "attach-title"} title={sessionId}>
session <span className="sid">{sessionId.slice(0, 8)}</span> session <span className="sid">{sessionId.slice(0, 8)}</span>
</span> </span>
); );
@@ -27,7 +27,7 @@ export function PaneTitle({ sessionId, session, className }: PaneTitleProps) {
const icon = tool ? toolIconSvg(tool) : null; const icon = tool ? toolIconSvg(tool) : null;
return ( return (
<span className={className ?? "at-title"} title={`${name} · ${session.command} (${sessionId})`}> <span className={className ?? "attach-title"} title={`${name} · ${session.command} (${sessionId})`}>
<span className="at-name">{name}</span> <span className="at-name">{name}</span>
{showCommand && ( {showCommand && (
<span className="at-cmd"> <span className="at-cmd">
+12 -9
View File
@@ -44,14 +44,17 @@ export interface SessionCardProps {
onRenameCancel(): void; onRenameCancel(): void;
} }
/** The fill ladder from docs/SIDEBAR.md "Which row is filled", furthest down /** The fill ladder from docs/SIDEBAR.md "Which row is filled" — the CSS
* wins. Written as one ordered run so the precedence is the order it reads in. */ * cascade in styles.css carries the actual fills via `.is-active`, `.is-open`,
function rowFill(p: SessionCardProps): string { * `.is-previewing` (all inside `:where()` so source order is the precedence).
if (p.active) return "bg-accent-fill"; * We just apply the right class names here. */
if (p.previewing) return "bg-preview-tint"; function rowClasses(p: SessionCardProps): string {
if (p.open) return "bg-accent/[0.06]"; const cls: string[] = [];
if (p.highlighted) return "bg-preview-tint"; if (p.active) cls.push("is-active");
return ""; if (p.open) cls.push("is-open");
if (p.previewing) cls.push("is-previewing");
if (p.highlighted) cls.push("is-highlighted");
return cls.join(" ");
} }
export function SessionCard(props: SessionCardProps) { export function SessionCard(props: SessionCardProps) {
@@ -62,7 +65,7 @@ export function SessionCard(props: SessionCardProps) {
return ( return (
<a <a
className={`session-card tree-leaf no-underline ${rowFill(props)} ${ className={`session-card tree-leaf no-underline ${rowClasses(props)} ${
running ? "" : "opacity-60" running ? "" : "opacity-60"
}`} }`}
href={`#/attach/${encodeURIComponent(s.id)}`} href={`#/attach/${encodeURIComponent(s.id)}`}