feat(inspector): update visual styling to match landing page (#166)

feat(inspector): update visual styling to match landing page

- Update color scheme to match website (black bg, white/10 borders)
- Add Open Sans font
- Update button styles (white primary buttons)
- Add collapsible tool results and status messages
- Replace avatar letters with icons (User, Settings, AlertTriangle)
- Add status dividers for session/turn events
- Update feature coverage badges to lighter grey
- Remove pill styling from event times
- Update popup menus to solid black background

feat(website): add Pi agent to hero diagram and update styling

- Add Pi agent with cyan color (#06B6D4) to the diagram
- Update layout to 3 agents on top row, 2 on bottom row
- Add backdrop-blur glass effects for modern look
- Add animated dot background that changes with active adapter
- Add scroll fade effect for hero section
- Update subtitle to include Pi in supported agents list
- Increase 'CONNECTED TO' label font size

feat(website): add site styling updates and SEO improvements

- Update component styling to match Rivet design (FAQ, FeatureGrid, etc.)
- Add SEO improvements (sitemap, robots.txt, meta tags, Open Graph)
- Remove CTASection component
- Update footer tagline
- Add Pi logo
This commit is contained in:
NicholasKissel 2026-02-12 01:42:54 +00:00
parent 89933c5f80
commit cdbe920070
19 changed files with 1158 additions and 867 deletions

View file

@ -1,102 +1,150 @@
import { useState } from "react";
import { getAvatarLabel, getMessageClass } from "./messageUtils";
import renderContentPart from "./renderContentPart";
import type { TimelineEntry } from "./types";
import { formatJson } from "../../utils/format";
import { AlertTriangle, Settings, ChevronRight, ChevronDown } from "lucide-react";
const CollapsibleMessage = ({
id,
icon,
label,
children,
className = ""
}: {
id: string;
icon: React.ReactNode;
label: string;
children: React.ReactNode;
className?: string;
}) => {
const [expanded, setExpanded] = useState(false);
return (
<div className={`collapsible-message ${className}`}>
<button className="collapsible-header" onClick={() => setExpanded(!expanded)}>
{expanded ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
{icon}
<span>{label}</span>
</button>
{expanded && <div className="collapsible-content">{children}</div>}
</div>
);
};
const ChatMessages = ({
entries,
sessionError,
eventError,
messagesEndRef
}: {
entries: TimelineEntry[];
sessionError: string | null;
eventError: string | null;
messagesEndRef: React.RefObject<HTMLDivElement>;
}) => {
return (
<div className="messages">
{entries.map((entry) => {
const messageClass = getMessageClass(entry);
if (entry.kind === "meta") {
return (
<div key={entry.id} className={`message ${messageClass}`}>
<div className="avatar">{getAvatarLabel(messageClass)}</div>
<div className="message-content">
<div className="message-meta">
<span>{entry.meta?.title ?? "Status"}</span>
</div>
{entry.meta?.detail && <div className="part-body">{entry.meta.detail}</div>}
const isError = entry.meta?.severity === "error";
const title = entry.meta?.title ?? "Status";
const isStatusDivider = ["Session Started", "Turn Started", "Turn Ended"].includes(title);
if (isStatusDivider) {
return (
<div key={entry.id} className="status-divider">
<div className="status-divider-line" />
<span className="status-divider-text">
<Settings size={12} />
{title}
</span>
<div className="status-divider-line" />
</div>
</div>
);
}
// Other status messages - collapsible
return (
<CollapsibleMessage
key={entry.id}
id={entry.id}
icon={isError ? <AlertTriangle size={14} className="error-icon" /> : <Settings size={14} className="system-icon" />}
label={title}
className={isError ? "error" : "system"}
>
{entry.meta?.detail && <div className="part-body">{entry.meta.detail}</div>}
</CollapsibleMessage>
);
}
if (entry.kind === "reasoning") {
const item = entry.item;
if (!item) return null;
const hasParts = (item.content ?? []).length > 0;
const isInProgress = item.status === "in_progress";
const isFailed = item.status === "failed";
const messageClass = getMessageClass(item);
const statusLabel = item.status !== "completed" ? item.status.replace("_", " ") : "";
const kindLabel = item.kind.replace("_", " ");
const isTool = messageClass === "tool";
// Tool results - collapsible
if (isTool) {
return (
<div key={entry.id} className="message assistant">
<div className="avatar">AI</div>
<div className="message-content">
<div className="message-meta">
<span>reasoning - {entry.reasoning?.visibility ?? "public"}</span>
</div>
<div className="part-body muted">{entry.reasoning?.text ?? ""}</div>
</div>
</div>
<CollapsibleMessage
key={entry.id}
id={entry.id}
icon={<span className="tool-icon">T</span>}
label={`${kindLabel}${statusLabel ? ` (${statusLabel})` : ""}`}
className="tool"
>
{hasParts ? (
(item.content ?? []).map(renderContentPart)
) : entry.deltaText ? (
<span>{entry.deltaText}</span>
) : (
<span className="muted">No content.</span>
)}
</CollapsibleMessage>
);
}
if (entry.kind === "tool") {
const isComplete = entry.toolStatus === "completed" || entry.toolStatus === "failed";
const isFailed = entry.toolStatus === "failed";
return (
<div key={entry.id} className={`message tool ${isFailed ? "error" : ""}`}>
<div className="avatar">{getAvatarLabel(isFailed ? "error" : "tool")}</div>
<div className="message-content">
return (
<div key={entry.id} className={`message ${messageClass} ${isFailed ? "error no-avatar" : ""}`}>
{!isFailed && <div className="avatar">{getAvatarLabel(messageClass)}</div>}
<div className="message-content">
{(item.kind !== "message" || item.status !== "completed") && (
<div className="message-meta">
<span>tool call - {entry.toolName}</span>
{entry.toolStatus && entry.toolStatus !== "completed" && (
<span className={`pill ${isFailed ? "danger" : "accent"}`}>
{entry.toolStatus.replace("_", " ")}
{isFailed && <AlertTriangle size={14} className="error-icon" />}
<span>{kindLabel}</span>
{statusLabel && (
<span className={`pill ${item.status === "failed" ? "danger" : "accent"}`}>
{statusLabel}
</span>
)}
</div>
{entry.toolInput && <pre className="code-block">{entry.toolInput}</pre>}
{isComplete && entry.toolOutput && (
<div className="part">
<div className="part-title">result</div>
<pre className="code-block">{entry.toolOutput}</pre>
</div>
)}
{!isComplete && !entry.toolInput && (
<span className="thinking-indicator">
<span className="thinking-dot" />
<span className="thinking-dot" />
<span className="thinking-dot" />
</span>
)}
</div>
</div>
);
}
// Message (user or assistant)
return (
<div key={entry.id} className={`message ${messageClass}`}>
<div className="avatar">{getAvatarLabel(messageClass)}</div>
<div className="message-content">
{entry.text ? (
<div className="part-body">{entry.text}</div>
) : (
)}
{hasParts ? (
(item.content ?? []).map(renderContentPart)
) : entry.deltaText ? (
<span>
{entry.deltaText}
{isInProgress && <span className="cursor" />}
</span>
) : isInProgress ? (
<span className="thinking-indicator">
<span className="thinking-dot" />
<span className="thinking-dot" />
<span className="thinking-dot" />
</span>
) : (
<span className="muted">No content yet.</span>
)}
</div>
</div>
);
})}
{sessionError && <div className="message-error">{sessionError}</div>}
{eventError && <div className="message-error">{eventError}</div>}
<div ref={messagesEndRef} />
</div>
);

View file

@ -1,17 +0,0 @@
import type { TimelineEntry } from "./types";
export const getMessageClass = (entry: TimelineEntry) => {
if (entry.kind === "tool") return "tool";
if (entry.kind === "meta") return entry.meta?.severity === "error" ? "error" : "system";
if (entry.kind === "reasoning") return "assistant";
if (entry.role === "user") return "user";
return "assistant";
};
export const getAvatarLabel = (messageClass: string) => {
if (messageClass === "user") return "U";
if (messageClass === "tool") return "T";
if (messageClass === "system") return "S";
if (messageClass === "error") return "!";
return "AI";
};

View file

@ -0,0 +1,20 @@
import type { UniversalItem } from "sandbox-agent";
import { Settings, AlertTriangle, User } from "lucide-react";
import type { ReactNode } from "react";
export const getMessageClass = (item: UniversalItem) => {
if (item.kind === "tool_call" || item.kind === "tool_result") return "tool";
if (item.kind === "system" || item.kind === "status") return "system";
if (item.role === "user") return "user";
if (item.role === "tool") return "tool";
if (item.role === "system") return "system";
return "assistant";
};
export const getAvatarLabel = (messageClass: string): ReactNode => {
if (messageClass === "user") return <User size={14} />;
if (messageClass === "tool") return "T";
if (messageClass === "system") return <Settings size={14} />;
if (messageClass === "error") return <AlertTriangle size={14} />;
return "AI";
};