feat(frontend): refresh dashboard, orchestrator, and AO logos (#317)

* feat(frontend): refresh dashboard, orchestrator, and AO logos

Replaces the dashboard and orchestrator glyphs and the AO brand logo
per issue #315.

- Dashboard buttons (sidebar action + Open Kanban) now use the
  asymmetric LayoutDashboard icon instead of the equal-cell LayoutGrid.
- Orchestrator buttons (sidebar action + every topbar badge/button) use
  a new org-chart OrchestratorIcon (parent fanning to three children),
  authored in lucide's stroke style since lucide has no matching glyph.
- The AO brand logo (sidebar header mark, landing nav, docs header) is
  now the pixel-mascot image; the old ao-logo.svg monogram is removed.

Closes #315

* chore: format with prettier [skip ci]

* fix(landing): use next/image and next/link for the AO logo and docs link

Addresses react-doctor review on #317: the Next.js landing app should use
next/image for the brand logo (optimized formats, responsive srcset, lazy
loading) and next/link for the internal /docs navigation (client-side
routing + prefetch) instead of plain <img>/<a>.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Harshit Singh Bhandari 2026-06-18 16:23:19 +05:30 committed by GitHub
parent 93123edd34
commit b37b6acefe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 70 additions and 44 deletions

View File

@ -1,4 +1,5 @@
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import Image from "next/image";
import { DocsLayout } from "fumadocs-ui/layouts/docs"; import { DocsLayout } from "fumadocs-ui/layouts/docs";
import { RootProvider } from "fumadocs-ui/provider"; import { RootProvider } from "fumadocs-ui/provider";
import type { LinkItemType } from "fumadocs-ui/layouts/shared"; import type { LinkItemType } from "fumadocs-ui/layouts/shared";
@ -82,7 +83,14 @@ export default function Layout({ children }: { children: ReactNode }) {
nav={{ nav={{
title: ( title: (
<span className="flex items-center gap-2 font-semibold"> <span className="flex items-center gap-2 font-semibold">
<img src="/ao-logo.svg" alt="" aria-hidden="true" width={22} height={22} className="h-[22px] w-[22px]" /> <Image
src="/ao-logo.png"
alt=""
aria-hidden
width={22}
height={22}
className="h-[22px] w-[22px] rounded-[5px] object-cover"
/>
<span className="text-[var(--color-text-primary)]">AO</span> <span className="text-[var(--color-text-primary)]">AO</span>
</span> </span>
), ),

View File

@ -1,5 +1,8 @@
"use client"; "use client";
import Image from "next/image";
import Link from "next/link";
function XIcon() { function XIcon() {
return ( return (
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"> <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
@ -31,17 +34,24 @@ export function LandingNav() {
href="#" href="#"
className="inline-flex items-center gap-2 text-base font-semibold text-white no-underline font-sans font-[680] tracking-tight" className="inline-flex items-center gap-2 text-base font-semibold text-white no-underline font-sans font-[680] tracking-tight"
> >
<img src="/ao-logo.svg" alt="" aria-hidden="true" width={28} height={28} className="h-7 w-7" /> <Image
src="/ao-logo.png"
alt=""
aria-hidden
width={28}
height={28}
className="h-7 w-7 rounded-md object-cover"
/>
Agent Orchestrator Agent Orchestrator
</a> </a>
<ul className="hidden md:flex items-center gap-8 list-none"> <ul className="hidden md:flex items-center gap-8 list-none">
<li> <li>
<a <Link
href="/docs" href="/docs"
className="text-sm text-[var(--landing-muted)] no-underline hover:text-white transition-colors" className="text-sm text-[var(--landing-muted)] no-underline hover:text-white transition-colors"
> >
Docs Docs
</a> </Link>
</li> </li>
<li> <li>
<a <a

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@ -1,6 +1,6 @@
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useNavigate, useParams } from "@tanstack/react-router"; import { useNavigate, useParams } from "@tanstack/react-router";
import { GitBranch, LayoutGrid, PanelRightClose, PanelRightOpen, Square, Waypoints } from "lucide-react"; import { GitBranch, LayoutDashboard, PanelRightClose, PanelRightOpen, Square } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { import {
findProjectOrchestrator, findProjectOrchestrator,
@ -14,6 +14,7 @@ import { useWorkspaceQuery, workspaceQueryKey } from "../hooks/useWorkspaceQuery
import { apiClient, apiErrorMessage } from "../lib/api-client"; import { apiClient, apiErrorMessage } from "../lib/api-client";
import { spawnOrchestrator } from "../lib/spawn-orchestrator"; import { spawnOrchestrator } from "../lib/spawn-orchestrator";
import { useUiStore } from "../stores/ui-store"; import { useUiStore } from "../stores/ui-store";
import { OrchestratorIcon } from "./icons";
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);
@ -105,7 +106,7 @@ export function ShellTopbar() {
· ·
</span> </span>
<span className="session-detail-mode-badge session-detail-mode-badge--neutral"> <span className="session-detail-mode-badge session-detail-mode-badge--neutral">
<Waypoints className="size-3 shrink-0" aria-hidden="true" /> <OrchestratorIcon className="size-3 shrink-0" aria-hidden="true" />
Orchestrator Orchestrator
</span> </span>
</div> </div>
@ -138,7 +139,7 @@ export function ShellTopbar() {
style={noDragStyle} style={noDragStyle}
type="button" type="button"
> >
<LayoutGrid className="h-3.5 w-3.5" aria-hidden="true" /> <LayoutDashboard className="h-3.5 w-3.5" aria-hidden="true" />
Open Kanban Open Kanban
</button> </button>
) : ( ) : (
@ -150,7 +151,7 @@ export function ShellTopbar() {
style={noDragStyle} style={noDragStyle}
type="button" type="button"
> >
<Waypoints className="h-3.5 w-3.5" aria-hidden="true" /> <OrchestratorIcon className="h-3.5 w-3.5" aria-hidden="true" />
{isSpawning ? "Spawning…" : "Open orchestrator"} {isSpawning ? "Spawning…" : "Open orchestrator"}
</button> </button>
)} )}
@ -190,7 +191,7 @@ export function ShellTopbar() {
style={noDragStyle} style={noDragStyle}
type="button" type="button"
> >
<Waypoints className="h-3.5 w-3.5" aria-hidden="true" /> <OrchestratorIcon className="h-3.5 w-3.5" aria-hidden="true" />
Orchestrator Orchestrator
</button> </button>
) : ( ) : (
@ -202,7 +203,7 @@ export function ShellTopbar() {
style={noDragStyle} style={noDragStyle}
type="button" type="button"
> >
<Waypoints className="h-3.5 w-3.5" aria-hidden="true" /> <OrchestratorIcon className="h-3.5 w-3.5" aria-hidden="true" />
{isSpawning ? "Spawning…" : "Spawn Orchestrator"} {isSpawning ? "Spawning…" : "Spawn Orchestrator"}
</button> </button>
) )

View File

@ -3,7 +3,7 @@ import { useNavigate, useParams, useRouterState } from "@tanstack/react-router";
import { import {
ChevronRight, ChevronRight,
GitPullRequest, GitPullRequest,
LayoutGrid, LayoutDashboard,
Moon, Moon,
MoreVertical, MoreVertical,
Plus, Plus,
@ -11,7 +11,6 @@ import {
Settings, Settings,
Sun, Sun,
Trash2, Trash2,
Waypoints,
} from "lucide-react"; } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { import {
@ -53,6 +52,8 @@ import {
useSidebar, useSidebar,
} from "./ui/sidebar"; } from "./ui/sidebar";
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
import { OrchestratorIcon } from "./icons";
import aoLogo from "../assets/ao-logo.png";
import { cn } from "../lib/utils"; import { cn } from "../lib/utils";
import { useUiStore } from "../stores/ui-store"; import { useUiStore } from "../stores/ui-store";
@ -158,8 +159,8 @@ export function Sidebar({ daemonStatus, workspaceError, workspaces, onCreateProj
<button <button
aria-label="Orchestrator board" aria-label="Orchestrator board"
className={cn( className={cn(
"grid h-[22px] w-[22px] shrink-0 place-items-center rounded-[6px] bg-accent text-accent-foreground", "grid h-[22px] w-[22px] shrink-0 place-items-center",
"group-data-[collapsible=icon]:size-9 group-data-[collapsible=icon]:rounded-lg group-data-[collapsible=icon]:bg-transparent group-data-[collapsible=icon]:text-current", "group-data-[collapsible=icon]:size-9 group-data-[collapsible=icon]:rounded-lg",
selection.isHome selection.isHome
? "group-data-[collapsible=icon]:bg-interactive-active" ? "group-data-[collapsible=icon]:bg-interactive-active"
: "group-data-[collapsible=icon]:hover:bg-interactive-hover", : "group-data-[collapsible=icon]:hover:bg-interactive-hover",
@ -167,9 +168,7 @@ export function Sidebar({ daemonStatus, workspaceError, workspaces, onCreateProj
onClick={selection.goHome} onClick={selection.goHome}
type="button" type="button"
> >
<span className="contents group-data-[collapsible=icon]:grid group-data-[collapsible=icon]:h-[22px] group-data-[collapsible=icon]:w-[22px] group-data-[collapsible=icon]:place-items-center group-data-[collapsible=icon]:rounded-[6px] group-data-[collapsible=icon]:bg-accent group-data-[collapsible=icon]:text-accent-foreground"> <img src={aoLogo} alt="" aria-hidden="true" className="h-[22px] w-[22px] rounded-[6px] object-cover" />
<Waypoints className="h-3.5 w-3.5" aria-hidden="true" />
</span>
</button> </button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side="right" hidden={state !== "collapsed"}> <TooltipContent side="right" hidden={state !== "collapsed"}>
@ -493,7 +492,7 @@ function ProjectItem({
onClick={() => selection.goProject(workspace.id)} onClick={() => selection.goProject(workspace.id)}
type="button" type="button"
> >
<LayoutGrid aria-hidden="true" /> <LayoutDashboard aria-hidden="true" />
</button> </button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent>Dashboard</TooltipContent> <TooltipContent>Dashboard</TooltipContent>
@ -507,7 +506,7 @@ function ProjectItem({
onClick={() => void openOrchestrator()} onClick={() => void openOrchestrator()}
type="button" type="button"
> >
<Waypoints aria-hidden="true" /> <OrchestratorIcon aria-hidden="true" />
</button> </button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>

View File

@ -0,0 +1,33 @@
import type { SVGProps } from "react";
// Orchestrator mark: a parent node fanning out to three child nodes, drawn in
// lucide's 24x24 stroke style so it drops into the same slots as the lucide
// icons (size comes from `className`/the parent's `[&_svg]:size-*`). Lucide has
// no 1-parent / 3-child hierarchy glyph, so we author this one to match the
// org-chart icon called for in the design.
export function OrchestratorIcon({ className, ...props }: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={className}
{...props}
>
<circle cx="12" cy="4" r="2" />
<circle cx="5" cy="20" r="2" />
<circle cx="12" cy="20" r="2" />
<circle cx="19" cy="20" r="2" />
<path d="M12 6v12" />
<path d="M5 11h14" />
<path d="M5 11v7" />
<path d="M19 11v7" />
</svg>
);
}