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:
parent
93123edd34
commit
b37b6acefe
|
|
@ -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>
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
|
@ -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>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue