Make board notifications Linux-specific
This commit is contained in:
parent
dcc3583be1
commit
52e6c7f3cd
|
|
@ -14,6 +14,12 @@ import { prDiffSummary, sessionPRDisplaySummaries } from "../lib/pr-display";
|
||||||
import { cn } from "../lib/utils";
|
import { cn } from "../lib/utils";
|
||||||
import { PRAttentionPanel, PRStatusStrip } from "./PRSummaryDisplay";
|
import { PRAttentionPanel, PRStatusStrip } from "./PRSummaryDisplay";
|
||||||
|
|
||||||
|
const isLinux =
|
||||||
|
typeof navigator !== "undefined" &&
|
||||||
|
((navigator as Navigator & { userAgentData?: { platform?: string } }).userAgentData?.platform ?? navigator.platform)
|
||||||
|
.toLowerCase()
|
||||||
|
.includes("linux");
|
||||||
|
|
||||||
type SessionsBoardProps = {
|
type SessionsBoardProps = {
|
||||||
/** When set, the board shows only this project's sessions. */
|
/** When set, the board shows only this project's sessions. */
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
|
|
@ -127,7 +133,7 @@ export function SessionsBoard({ projectId }: SessionsBoardProps) {
|
||||||
|
|
||||||
const actions = projectId ? (
|
const actions = projectId ? (
|
||||||
<>
|
<>
|
||||||
<NotificationCenter />
|
{isLinux ? <NotificationCenter /> : null}
|
||||||
<button
|
<button
|
||||||
aria-label="New task"
|
aria-label="New task"
|
||||||
className="dashboard-app-header__accent-btn"
|
className="dashboard-app-header__accent-btn"
|
||||||
|
|
@ -148,9 +154,9 @@ export function SessionsBoard({ projectId }: SessionsBoardProps) {
|
||||||
{isSpawning ? "Spawning..." : orchestrator ? "Orchestrator" : "Spawn Orchestrator"}
|
{isSpawning ? "Spawning..." : orchestrator ? "Orchestrator" : "Spawn Orchestrator"}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : isLinux ? (
|
||||||
<NotificationCenter />
|
<NotificationCenter />
|
||||||
);
|
) : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full min-h-0 flex-col bg-background text-foreground">
|
<div className="flex h-full min-h-0 flex-col bg-background text-foreground">
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { useNavigate, useParams } from "@tanstack/react-router";
|
import { useNavigate, useParams } from "@tanstack/react-router";
|
||||||
import { GitBranch, LayoutDashboard, PanelRightClose, PanelRightOpen, Plus, Square, Trash2 } from "lucide-react";
|
import { GitBranch, LayoutDashboard, PanelRightClose, PanelRightOpen, Plus, Square, Trash2 } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { NotificationCenter } from "./NotificationCenter";
|
||||||
import {
|
import {
|
||||||
findProjectOrchestrator,
|
findProjectOrchestrator,
|
||||||
isOrchestratorSession,
|
isOrchestratorSession,
|
||||||
|
|
@ -20,6 +21,11 @@ import { NewTaskDialog } from "./NewTaskDialog";
|
||||||
import { cn } from "../lib/utils";
|
import { cn } from "../lib/utils";
|
||||||
|
|
||||||
const isMac = typeof navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(navigator.userAgent);
|
const isMac = typeof navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(navigator.userAgent);
|
||||||
|
const isLinux =
|
||||||
|
typeof navigator !== "undefined" &&
|
||||||
|
((navigator as Navigator & { userAgentData?: { platform?: string } }).userAgentData?.platform ?? navigator.platform)
|
||||||
|
.toLowerCase()
|
||||||
|
.includes("linux");
|
||||||
const dragStyle = isMac ? ({ WebkitAppRegion: "drag" } as React.CSSProperties) : undefined;
|
const dragStyle = isMac ? ({ WebkitAppRegion: "drag" } as React.CSSProperties) : undefined;
|
||||||
const noDragStyle = isMac ? ({ WebkitAppRegion: "no-drag" } as React.CSSProperties) : undefined;
|
const noDragStyle = isMac ? ({ WebkitAppRegion: "no-drag" } as React.CSSProperties) : undefined;
|
||||||
|
|
||||||
|
|
@ -69,10 +75,12 @@ export function ShellTopbar() {
|
||||||
// removed, or data still loading) shows an empty crumb — never the raw
|
// removed, or data still loading) shows an empty crumb — never the raw
|
||||||
// route slug. "agent-orchestrator" is the root-board crumb only.
|
// route slug. "agent-orchestrator" is the root-board crumb only.
|
||||||
const projectId = session?.workspaceId ?? params.projectId;
|
const projectId = session?.workspaceId ?? params.projectId;
|
||||||
const projectLabel = session?.workspaceName ?? "";
|
const isProjectBoardRoute = !isSessionRoute && Boolean(projectId);
|
||||||
|
const project = projectId ? all.find((workspace) => workspace.id === projectId) : undefined;
|
||||||
|
const projectLabel = project?.name ?? session?.workspaceName ?? (projectId ? "" : "agent-orchestrator");
|
||||||
const orchestrator = projectId ? findProjectOrchestrator(all, projectId) : undefined;
|
const orchestrator = projectId ? findProjectOrchestrator(all, projectId) : undefined;
|
||||||
|
|
||||||
if (!isSessionRoute) {
|
if (isLinux && !isSessionRoute) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,7 +141,7 @@ export function ShellTopbar() {
|
||||||
return (
|
return (
|
||||||
<header className={cn("dashboard-app-header", isMac && "is-under-titlebar-nav")} style={dragStyle}>
|
<header className={cn("dashboard-app-header", isMac && "is-under-titlebar-nav")} style={dragStyle}>
|
||||||
<div className="session-topbar__lead">
|
<div className="session-topbar__lead">
|
||||||
{isOrchestrator ? (
|
{isSessionRoute && isOrchestrator ? (
|
||||||
<div className="topbar-project-pills-group">
|
<div className="topbar-project-pills-group">
|
||||||
<div className="topbar-project-line">
|
<div className="topbar-project-line">
|
||||||
<span className="dashboard-app-header__project">{projectLabel}</span>
|
<span className="dashboard-app-header__project">{projectLabel}</span>
|
||||||
|
|
@ -146,7 +154,7 @@ export function ShellTopbar() {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : isSessionRoute ? (
|
||||||
<div className="session-topbar__identity">
|
<div className="session-topbar__identity">
|
||||||
<div className="session-topbar__branch">
|
<div className="session-topbar__branch">
|
||||||
<GitBranch className="h-3 w-3 shrink-0" aria-hidden="true" />
|
<GitBranch className="h-3 w-3 shrink-0" aria-hidden="true" />
|
||||||
|
|
@ -154,13 +162,18 @@ export function ShellTopbar() {
|
||||||
</div>
|
</div>
|
||||||
{session ? <SessionStatusPill session={session} /> : null}
|
{session ? <SessionStatusPill session={session} /> : null}
|
||||||
</div>
|
</div>
|
||||||
|
) : isProjectBoardRoute ? null : (
|
||||||
|
<div className="topbar-project-line">
|
||||||
|
<span className="dashboard-app-header__project">{projectLabel}</span>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="dashboard-app-header__spacer" />
|
<div className="dashboard-app-header__spacer" />
|
||||||
|
|
||||||
<div className="dashboard-app-header__actions">
|
<div className="dashboard-app-header__actions">
|
||||||
{isOrchestrator ? (
|
{!isLinux ? <NotificationCenter style={noDragStyle} /> : null}
|
||||||
|
{isSessionRoute && isOrchestrator ? (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
aria-label="New task"
|
aria-label="New task"
|
||||||
|
|
@ -187,7 +200,7 @@ export function ShellTopbar() {
|
||||||
{/* Kill control sits beside the orchestrator link for active workers —
|
{/* Kill control sits beside the orchestrator link for active workers —
|
||||||
moved here from the inspector's Summary "Danger zone". */}
|
moved here from the inspector's Summary "Danger zone". */}
|
||||||
{!isOrchestrator && session && sessionIsActive(session) ? <TopbarKillButton session={session} /> : null}
|
{!isOrchestrator && session && sessionIsActive(session) ? <TopbarKillButton session={session} /> : null}
|
||||||
{!isOrchestrator && (
|
{isSessionRoute && !isOrchestrator && (
|
||||||
<button
|
<button
|
||||||
aria-label="Open orchestrator"
|
aria-label="Open orchestrator"
|
||||||
className="dashboard-app-header__primary-btn dashboard-app-header__primary-btn--compact"
|
className="dashboard-app-header__primary-btn dashboard-app-header__primary-btn--compact"
|
||||||
|
|
@ -201,7 +214,7 @@ export function ShellTopbar() {
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{/* Inspector collapse (worker sessions only — orchestrators have no rail). */}
|
{/* Inspector collapse (worker sessions only — orchestrators have no rail). */}
|
||||||
{!isOrchestrator && (
|
{isSessionRoute && !isOrchestrator && (
|
||||||
<button
|
<button
|
||||||
aria-label={isInspectorOpen ? "Close inspector panel" : "Open inspector panel"}
|
aria-label={isInspectorOpen ? "Close inspector panel" : "Open inspector panel"}
|
||||||
aria-pressed={isInspectorOpen}
|
aria-pressed={isInspectorOpen}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,12 @@ function errorMessage(error: unknown) {
|
||||||
return error instanceof Error ? error.message : "Could not load projects";
|
return error instanceof Error ? error.message : "Could not load projects";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isLinux =
|
||||||
|
typeof navigator !== "undefined" &&
|
||||||
|
((navigator as Navigator & { userAgentData?: { platform?: string } }).userAgentData?.platform ?? navigator.platform)
|
||||||
|
.toLowerCase()
|
||||||
|
.includes("linux");
|
||||||
|
|
||||||
// Persistent app shell: the Sidebar + shared state survive route changes; only
|
// Persistent app shell: the Sidebar + shared state survive route changes; only
|
||||||
// the <Outlet> content (board / session / settings / …) swaps. Lifted out of
|
// the <Outlet> content (board / session / settings / …) swaps. Lifted out of
|
||||||
// the old single <App>, with selection now owned by the router (route params)
|
// the old single <App>, with selection now owned by the router (route params)
|
||||||
|
|
@ -189,7 +195,7 @@ function ShellLayout() {
|
||||||
>
|
>
|
||||||
<Sidebar
|
<Sidebar
|
||||||
daemonStatus={daemonStatus}
|
daemonStatus={daemonStatus}
|
||||||
underTopbar={isSessionRoute}
|
underTopbar={isLinux ? isSessionRoute : true}
|
||||||
onCreateProject={createProject}
|
onCreateProject={createProject}
|
||||||
onRemoveProject={removeProject}
|
onRemoveProject={removeProject}
|
||||||
workspaceError={workspaceQuery.isError ? errorMessage(workspaceQuery.error) : undefined}
|
workspaceError={workspaceQuery.isError ? errorMessage(workspaceQuery.error) : undefined}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue