Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import {
Lock,
MessageCircle,
MessagesSquare,
Moon,
Plus,
Send,
Sun,
ThumbsDown,
ThumbsUp,
UserCheck,
Expand All @@ -23,6 +25,7 @@ import { readConversationSinceBreakpoint } from "./domain";

type View = "overview" | "forums" | "direct" | "suggestions" | "onboarding" | "gates" | "profile";
type AgentStatus = "pending" | "approved" | "suspended";
type ThemeMode = "day" | "night";
type ForumDraft = {
slug: string;
name: string;
Expand Down Expand Up @@ -50,6 +53,29 @@ const emptyState: AgentCommsState = {
};

const useDemoData = import.meta.env.DEV && new URLSearchParams(window.location.search).get("demo") === "1";
const themePreferenceKey = "agent-comms-theme-mode";

const nightModeTheme: Record<string, string> = {
"--color-bg": "#101714",
"--color-surface": "#18231f",
"--color-surface-hover": "#22312b",
"--color-sidebar": "#0b1210",
"--color-sidebar-hover": "#1b2a25",
"--color-text": "#edf5ee",
"--color-text-secondary": "#bdcbc2",
"--color-muted": "#93a39a",
"--color-line": "#33413b",
"--color-line-strong": "#46554e",
"--color-accent": "#8bc7a7",
"--color-accent-soft": "#294338",
"--color-warning": "#a98135",
"--color-danger": "#ef7a64",
"--color-ink": "#edf5ee",
"--color-inverse": "#edf5ee",
"--color-inverse-muted": "#9aada4",
"--color-inverse-accent": "#b9e1c9",
"--shadow-card": "0 1px 2px rgba(0, 0, 0, 0.22), 0 18px 34px -24px rgba(0, 0, 0, 0.82)",
};

type LiveConversationSession = {
id: string;
Expand Down Expand Up @@ -234,6 +260,12 @@ function shellSingleQuote(value: string) {
return `'${value.replaceAll("'", "'\\''")}'`;
}

function getInitialThemeMode(): ThemeMode {
const stored = localStorage.getItem(themePreferenceKey);
if (stored === "day" || stored === "night") return stored;
return window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "night" : "day";
}

function onboardingCorrectionPrompt(agent: AgentIdentity) {
const authStatus = agent.onboardingAuth?.status ?? "missing";
const statusText = authStatus.replace("_", " ");
Expand Down Expand Up @@ -1455,11 +1487,13 @@ export function App() {
const [mintedTokens, setMintedTokens] = useState<Record<string, { token: string; copied?: boolean; fileCopied?: boolean } | undefined>>({});
const [liveSessions, setLiveSessions] = useState<LiveConversationSession[]>([]);
const [operatorToken] = useState(() => localStorage.getItem("agent-comms-operator-token") ?? "");
const [themeMode, setThemeMode] = useState<ThemeMode>(getInitialThemeMode);
const [apiStatus, setApiStatus] = useState(useDemoData ? "demo data" : "loading durable storage");
const [actionStatus, setActionStatus] = useState("");
const refreshSequenceRef = useRef(0);
const mutationEpochRef = useRef(0);
const activeOperatorMutationsRef = useRef(0);
const appTheme = themeMode === "night" ? { ...branding.theme, ...nightModeTheme } : branding.theme;

const beginOperatorMutation = useCallback(() => {
mutationEpochRef.current += 1;
Expand Down Expand Up @@ -1684,6 +1718,11 @@ export function App() {
localStorage.setItem("agent-comms-read-thread-activity-ids", JSON.stringify(readThreadActivityIds));
}, [readThreadActivityIds]);

useEffect(() => {
localStorage.setItem(themePreferenceKey, themeMode);
document.documentElement.dataset.theme = themeMode;
}, [themeMode]);

const latestConversationMessageIds = Object.fromEntries(
state.directConversations.map((conversation) => [
conversation.id,
Expand Down Expand Up @@ -2128,7 +2167,7 @@ export function App() {
};

return (
<main className="app-shell" style={branding.theme}>
<main className="app-shell" data-theme={themeMode} style={appTheme}>
<nav className="sidebar" aria-label="Main navigation">
<div className={branding.logoUrl ? "brand has-logo" : "brand"}>
{branding.logoUrl ? (
Expand Down Expand Up @@ -2161,6 +2200,16 @@ export function App() {
<p className="eyebrow">{branding.eyebrow}</p>
<h1>{branding.title}</h1>
</div>
<div className="topbar-actions">
<button
aria-label={themeMode === "night" ? "Switch to day mode" : "Switch to night mode"}
onClick={() => setThemeMode((current) => (current === "night" ? "day" : "night"))}
title={themeMode === "night" ? "Switch to day mode" : "Switch to night mode"}
type="button"
>
{themeMode === "night" ? <Sun aria-hidden="true" /> : <Moon aria-hidden="true" />}
</button>
</div>
</header>
<p className="api-status">Data source: {apiStatus}{actionStatus ? `; ${actionStatus}` : ""}</p>
{view === "overview" ? (
Expand Down
11 changes: 11 additions & 0 deletions src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
--color-accent: #2f6f55;
--color-accent-soft: #dcebbb;
--color-warning: #f3dfb3;
--color-danger: #cc4729;
--color-ink: #193428;
--color-inverse: #f8f4ea;
--color-inverse-muted: #aec1b6;
--color-inverse-accent: #e1ecb8;
Expand All @@ -33,6 +35,7 @@ body {
margin: 0;
min-width: 320px;
min-height: 100vh;
background: var(--color-bg);
}

button {
Expand All @@ -44,6 +47,7 @@ button {
display: grid;
grid-template-columns: 280px minmax(0, 1fr);
min-height: 100vh;
background: var(--color-bg);
}

.sidebar {
Expand Down Expand Up @@ -184,6 +188,13 @@ svg {
color: var(--color-sidebar);
}

.topbar-actions button:hover,
.topbar-actions button:focus-visible {
border-color: var(--color-accent);
color: var(--color-accent);
outline: 0;
}

.section-title .section-action {
width: auto;
min-width: max-content;
Expand Down
Loading