summaryrefslogtreecommitdiff
path: root/makima/frontend
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-22 16:39:15 +0000
committerGitHub <noreply@github.com>2026-02-22 16:39:15 +0000
commita8dd432fd58a3036cf952eec691981dff43a7c7e (patch)
tree925d67ce3f4a00b744a320c920be57f13f5b15a5 /makima/frontend
parent0c00b6aa26baf49ba02164cd44aab16f9c804bbd (diff)
downloadsoryu-a8dd432fd58a3036cf952eec691981dff43a7c7e.tar.gz
soryu-a8dd432fd58a3036cf952eec691981dff43a7c7e.zip
fix: remove duplicate daemon page, add ARM64 binary, use makima.jp URLs (#78)
* feat: soryu-co/soryu - makima: Add macOS ARM64 binary to daemon download bundle * feat: soryu-co/soryu - makima: Remove duplicate Daemon nav entry and route * feat: soryu-co/soryu - makima: Update download URLs to use makima.jp hosted instance
Diffstat (limited to 'makima/frontend')
-rw-r--r--makima/frontend/src/components/NavStrip.tsx1
-rw-r--r--makima/frontend/src/main.tsx9
-rw-r--r--makima/frontend/src/routes/daemon.tsx746
3 files changed, 0 insertions, 756 deletions
diff --git a/makima/frontend/src/components/NavStrip.tsx b/makima/frontend/src/components/NavStrip.tsx
index 1bd0891..9556458 100644
--- a/makima/frontend/src/components/NavStrip.tsx
+++ b/makima/frontend/src/components/NavStrip.tsx
@@ -17,7 +17,6 @@ const NAV_LINKS: NavLink[] = [
{ label: "Mesh", href: "/mesh", requiresAuth: true },
{ label: "Daemons", href: "/daemons", requiresAuth: true },
{ label: "History", href: "/history", requiresAuth: true },
- { label: "Daemon", href: "/daemon", requiresAuth: true },
];
export function NavStrip() {
diff --git a/makima/frontend/src/main.tsx b/makima/frontend/src/main.tsx
index a75d6a0..32c05ba 100644
--- a/makima/frontend/src/main.tsx
+++ b/makima/frontend/src/main.tsx
@@ -21,7 +21,6 @@ import SettingsPage from "./routes/settings";
import ContractFilePage from "./routes/contract-file";
import SpeakPage from "./routes/speak";
import DirectivesPage from "./routes/directives";
-import DaemonPage from "./routes/daemon";
createRoot(document.getElementById("root")!).render(
<StrictMode>
@@ -163,14 +162,6 @@ createRoot(document.getElementById("root")!).render(
}
/>
<Route
- path="/daemon"
- element={
- <ProtectedRoute>
- <DaemonPage />
- </ProtectedRoute>
- }
- />
- <Route
path="/speak"
element={
<ProtectedRoute>
diff --git a/makima/frontend/src/routes/daemon.tsx b/makima/frontend/src/routes/daemon.tsx
deleted file mode 100644
index 66154ad..0000000
--- a/makima/frontend/src/routes/daemon.tsx
+++ /dev/null
@@ -1,746 +0,0 @@
-import { useState, useEffect, useCallback } from "react";
-import { useNavigate } from "react-router";
-import { Masthead } from "../components/Masthead";
-import { useAuth } from "../contexts/AuthContext";
-import {
- listDaemons,
- restartDaemon,
- type Daemon,
-} from "../lib/api";
-
-// =============================================================================
-// Types
-// =============================================================================
-
-interface GitHubAsset {
- name: string;
- browser_download_url: string;
- size: number;
-}
-
-interface GitHubRelease {
- tag_name: string;
- name: string;
- published_at: string;
- html_url: string;
- assets: GitHubAsset[];
-}
-
-interface PlatformDownload {
- label: string;
- arch: string;
- pattern: string;
- asset: GitHubAsset | null;
- recommended: boolean;
-}
-
-// =============================================================================
-// Helpers
-// =============================================================================
-
-function detectPlatform(): string {
- const ua = navigator.userAgent.toLowerCase();
- const platform = navigator.platform?.toLowerCase() || "";
-
- if (platform.includes("mac") || ua.includes("macintosh")) {
- // Check for Apple Silicon
- // navigator.platform is "MacIntel" even on ARM for some browsers,
- // but we can check userAgent for hints or default to arm64 for modern Macs
- if (
- ua.includes("arm") ||
- ua.includes("aarch64") ||
- // Chrome 93+ on ARM Macs reports this
- (typeof navigator !== "undefined" &&
- "userAgentData" in navigator &&
- // @ts-expect-error -- userAgentData may not be typed
- navigator.userAgentData?.platform === "macOS")
- ) {
- return "macos-arm64";
- }
- return "macos-arm64"; // Default to ARM64 for modern Macs
- }
-
- if (platform.includes("linux") || ua.includes("linux")) {
- return "linux-x86_64";
- }
-
- return "linux-x86_64"; // fallback
-}
-
-function formatBytes(bytes: number): string {
- if (bytes === 0) return "0 B";
- const k = 1024;
- const sizes = ["B", "KB", "MB", "GB"];
- const i = Math.floor(Math.log(bytes) / Math.log(k));
- return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
-}
-
-function formatDate(dateStr: string): string {
- return new Date(dateStr).toLocaleDateString("en-US", {
- year: "numeric",
- month: "short",
- day: "numeric",
- });
-}
-
-// =============================================================================
-// Sub-components
-// =============================================================================
-
-function SectionHeader({ children }: { children: React.ReactNode }) {
- return (
- <h2 className="text-[11px] font-mono uppercase tracking-wide text-[#8899aa] mb-3 pb-2 border-b border-[rgba(117,170,252,0.15)]">
- {children}
- </h2>
- );
-}
-
-function ErrorAlert({ children }: { children: React.ReactNode }) {
- return (
- <div className="border border-red-700/50 bg-red-900/20 text-red-400 px-3 py-2 mb-4 font-mono text-xs">
- {children}
- </div>
- );
-}
-
-function CodeBlock({ children }: { children: React.ReactNode }) {
- return (
- <code className="block bg-black/50 px-3 py-2 text-[11px] font-mono text-green-400 mb-2 overflow-x-auto">
- {children}
- </code>
- );
-}
-
-function StepNumber({ n }: { n: number }) {
- return (
- <span className="inline-flex items-center justify-center w-5 h-5 border border-[rgba(117,170,252,0.35)] text-[10px] font-mono text-[#75aafc] mr-2 shrink-0">
- {n}
- </span>
- );
-}
-
-// =============================================================================
-// Download Section
-// =============================================================================
-
-function DownloadSection() {
- const [release, setRelease] = useState<GitHubRelease | null>(null);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState<string | null>(null);
- const [userPlatform] = useState(detectPlatform);
-
- useEffect(() => {
- const fetchRelease = async () => {
- try {
- setLoading(true);
- setError(null);
- const res = await fetch(
- "https://api.github.com/repos/soryu-co/makima/releases/latest"
- );
- if (!res.ok) {
- throw new Error(`GitHub API returned ${res.status}`);
- }
- const data: GitHubRelease = await res.json();
- setRelease(data);
- } catch (err) {
- setError(
- err instanceof Error ? err.message : "Failed to fetch release info"
- );
- } finally {
- setLoading(false);
- }
- };
- fetchRelease();
- }, []);
-
- const platforms: PlatformDownload[] = [
- {
- label: "Linux x86_64",
- arch: "linux-x86_64",
- pattern: "linux-x86_64.tar.gz",
- asset: null,
- recommended: userPlatform === "linux-x86_64",
- },
- {
- label: "macOS Intel (x86_64)",
- arch: "macos-x86_64",
- pattern: "macos-x86_64.tar.gz",
- asset: null,
- recommended: userPlatform === "macos-x86_64",
- },
- {
- label: "macOS Apple Silicon (ARM64)",
- arch: "macos-arm64",
- pattern: "macos-arm64.tar.gz",
- asset: null,
- recommended: userPlatform === "macos-arm64",
- },
- ];
-
- // Match assets to platforms
- if (release) {
- for (const p of platforms) {
- p.asset =
- release.assets.find((a) => a.name.includes(p.pattern)) || null;
- }
- }
-
- // Sort recommended first
- const sortedPlatforms = [...platforms].sort(
- (a, b) => (b.recommended ? 1 : 0) - (a.recommended ? 1 : 0)
- );
-
- return (
- <section className="border border-[rgba(117,170,252,0.25)] bg-[#0d1b2d] p-4">
- <SectionHeader>Download Daemon</SectionHeader>
-
- {loading && (
- <p className="text-[#7788aa] font-mono text-xs">
- Fetching latest release...
- </p>
- )}
-
- {error && <ErrorAlert>Failed to load release: {error}</ErrorAlert>}
-
- {release && (
- <>
- <div className="flex items-center justify-between mb-4">
- <div className="flex items-center gap-3">
- <span className="text-[#9bc3ff] font-mono text-sm">
- {release.tag_name}
- </span>
- <span className="text-[#556677] font-mono text-[10px]">
- {formatDate(release.published_at)}
- </span>
- </div>
- <a
- href="https://github.com/soryu-co/makima/releases"
- target="_blank"
- rel="noopener noreferrer"
- className="text-[10px] font-mono text-[#75aafc] hover:text-[#9bc3ff] transition-colors"
- >
- All Releases &rarr;
- </a>
- </div>
-
- <div className="space-y-2">
- {sortedPlatforms.map((p) => (
- <div
- key={p.arch}
- className={`border p-3 flex items-center justify-between ${
- p.recommended
- ? "border-[rgba(117,170,252,0.5)] bg-[#0a1628]"
- : "border-[rgba(117,170,252,0.15)] bg-[#0a1525]"
- }`}
- >
- <div className="flex items-center gap-3">
- <span className="font-mono text-xs text-[#9bc3ff]">
- {p.label}
- </span>
- {p.recommended && (
- <span className="text-[9px] font-mono uppercase px-1.5 py-0.5 border border-[rgba(117,170,252,0.4)] text-[#75aafc] bg-[rgba(117,170,252,0.08)]">
- Detected
- </span>
- )}
- {p.asset && (
- <span className="text-[10px] font-mono text-[#556677]">
- {formatBytes(p.asset.size)}
- </span>
- )}
- </div>
- {p.asset ? (
- <a
- href={p.asset.browser_download_url}
- className={`text-[10px] font-mono uppercase px-3 py-1.5 border transition-colors ${
- p.recommended
- ? "text-[#9bc3ff] border-[rgba(117,170,252,0.5)] bg-[rgba(117,170,252,0.1)] hover:bg-[rgba(117,170,252,0.2)]"
- : "text-[#75aafc] border-[rgba(117,170,252,0.25)] hover:bg-[rgba(117,170,252,0.1)]"
- }`}
- download
- >
- Download
- </a>
- ) : (
- <span className="text-[10px] font-mono text-[#556677]">
- Not available
- </span>
- )}
- </div>
- ))}
- </div>
- </>
- )}
- </section>
- );
-}
-
-// =============================================================================
-// Setup Instructions Section
-// =============================================================================
-
-function SetupSection() {
- const [showConfig, setShowConfig] = useState(false);
-
- return (
- <section className="border border-[rgba(117,170,252,0.25)] bg-[#0d1b2d] p-4">
- <SectionHeader>Setup Instructions</SectionHeader>
- <div className="space-y-3">
- <div className="flex items-start">
- <StepNumber n={1} />
- <div className="flex-1">
- <p className="text-[#7788aa] font-mono text-[10px] mb-1">
- Download the binary for your platform above
- </p>
- </div>
- </div>
-
- <div className="flex items-start">
- <StepNumber n={2} />
- <div className="flex-1">
- <p className="text-[#7788aa] font-mono text-[10px] mb-1">
- Extract the archive
- </p>
- <CodeBlock>tar xzf makima-*.tar.gz</CodeBlock>
- </div>
- </div>
-
- <div className="flex items-start">
- <StepNumber n={3} />
- <div className="flex-1">
- <p className="text-[#7788aa] font-mono text-[10px] mb-1">
- Move to PATH
- </p>
- <CodeBlock>sudo mv makima /usr/local/bin/</CodeBlock>
- </div>
- </div>
-
- <div className="flex items-start">
- <StepNumber n={4} />
- <div className="flex-1">
- <p className="text-[#7788aa] font-mono text-[10px] mb-1">
- Set your API key (
- <a
- href="/settings"
- className="text-[#75aafc] hover:text-[#9bc3ff] transition-colors"
- >
- generate one in Settings
- </a>
- )
- </p>
- <CodeBlock>export MAKIMA_API_KEY="your-key"</CodeBlock>
- </div>
- </div>
-
- <div className="flex items-start">
- <StepNumber n={5} />
- <div className="flex-1">
- <p className="text-[#7788aa] font-mono text-[10px] mb-1">
- Set server URL
- </p>
- <CodeBlock>
- export MAKIMA_DAEMON_SERVER_URL="ws://your-server:8080"
- </CodeBlock>
- </div>
- </div>
-
- <div className="flex items-start">
- <StepNumber n={6} />
- <div className="flex-1">
- <p className="text-[#7788aa] font-mono text-[10px] mb-1">
- Run the daemon
- </p>
- <CodeBlock>makima daemon</CodeBlock>
- </div>
- </div>
- </div>
-
- {/* Config file alternative */}
- <div className="mt-4 pt-3 border-t border-[rgba(117,170,252,0.1)]">
- <button
- onClick={() => setShowConfig(!showConfig)}
- className="text-[10px] font-mono text-[#75aafc] hover:text-[#9bc3ff] transition-colors bg-transparent border-none cursor-pointer p-0"
- >
- {showConfig ? "- Hide" : "+"} Config file alternative
- </button>
- {showConfig && (
- <div className="mt-3">
- <p className="text-[#7788aa] font-mono text-[10px] mb-2">
- Create <code className="text-green-400">makima-daemon.toml</code>{" "}
- in the working directory:
- </p>
- <code className="block bg-black/50 px-3 py-2 text-[11px] font-mono text-green-400 whitespace-pre overflow-x-auto">
- {`[daemon]
-api_key = "your-key"
-server_url = "ws://your-server:8080"
-max_concurrent_tasks = 4`}
- </code>
- </div>
- )}
- </div>
- </section>
- );
-}
-
-// =============================================================================
-// Cloudflare Edge Deployment Section
-// =============================================================================
-
-function CloudflareAgentSection() {
- const [showSetup, setShowSetup] = useState(false);
-
- const benefits = [
- {
- label: "Global edge presence",
- desc: "Lower latency from 300+ Cloudflare locations worldwide",
- },
- {
- label: "Auto-scaling & hibernation",
- desc: "Cost-efficient — only runs when needed",
- },
- {
- label: "WebSocket relay",
- desc: "Coordinate remote daemon instances through persistent connections",
- },
- {
- label: "Durable Objects",
- desc: "Built on Cloudflare's stateful edge compute primitives",
- },
- ];
-
- return (
- <section className="border border-[rgba(117,170,252,0.25)] bg-[#0d1b2d] p-4">
- <SectionHeader>Edge Deployment</SectionHeader>
-
- <p className="text-[#7788aa] font-mono text-[10px] mb-4 leading-relaxed">
- Deploy a lightweight Makima relay agent on Cloudflare's edge network for
- global, low-latency daemon coordination. Ideal for distributed teams or
- production deployments requiring high availability.
- </p>
-
- {/* Benefits */}
- <div className="space-y-2 mb-4">
- {benefits.map((b) => (
- <div
- key={b.label}
- className="flex items-start gap-2 text-[10px] font-mono"
- >
- <span className="text-[#75aafc] mt-px shrink-0">&#x25B8;</span>
- <div>
- <span className="text-[#9bc3ff]">{b.label}</span>
- <span className="text-[#556677] ml-1">— {b.desc}</span>
- </div>
- </div>
- ))}
- </div>
-
- {/* Quick Setup */}
- <div className="border-t border-[rgba(117,170,252,0.1)] pt-3">
- <button
- onClick={() => setShowSetup(!showSetup)}
- className="text-[10px] font-mono text-[#75aafc] hover:text-[#9bc3ff] transition-colors bg-transparent border-none cursor-pointer p-0"
- >
- {showSetup ? "- Hide" : "+"} Quick setup
- </button>
- {showSetup && (
- <div className="mt-3 space-y-3">
- <div className="flex items-start">
- <StepNumber n={1} />
- <div className="flex-1">
- <p className="text-[#7788aa] font-mono text-[10px] mb-1">
- Navigate to the Cloudflare agent directory
- </p>
- <CodeBlock>cd makima/cloudflare-agent</CodeBlock>
- </div>
- </div>
- <div className="flex items-start">
- <StepNumber n={2} />
- <div className="flex-1">
- <p className="text-[#7788aa] font-mono text-[10px] mb-1">
- Run the setup script
- </p>
- <CodeBlock>./setup.sh</CodeBlock>
- </div>
- </div>
- <div className="flex items-start">
- <StepNumber n={3} />
- <div className="flex-1">
- <p className="text-[#7788aa] font-mono text-[10px] mb-1">
- Deploy to Cloudflare
- </p>
- <CodeBlock>npx wrangler deploy</CodeBlock>
- </div>
- </div>
- </div>
- )}
- </div>
-
- {/* Link to repo */}
- <div className="mt-4 pt-3 border-t border-[rgba(117,170,252,0.1)] flex items-center justify-between">
- <span className="text-[10px] font-mono text-[#556677]">
- Full documentation & source
- </span>
- <a
- href="https://github.com/soryu-co/soryu/tree/master/makima/cloudflare-agent"
- target="_blank"
- rel="noopener noreferrer"
- className="text-[10px] font-mono text-[#75aafc] hover:text-[#9bc3ff] transition-colors px-3 py-1.5 border border-[rgba(117,170,252,0.25)] hover:bg-[rgba(117,170,252,0.1)]"
- >
- View on GitHub &rarr;
- </a>
- </div>
- </section>
- );
-}
-
-// =============================================================================
-// Connected Daemons Section
-// =============================================================================
-
-function ConnectedDaemonsSection() {
- const [daemons, setDaemons] = useState<Daemon[]>([]);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState<string | null>(null);
- const [restartingDaemonId, setRestartingDaemonId] = useState<string | null>(
- null
- );
- const [restartConfirmDaemonId, setRestartConfirmDaemonId] = useState<
- string | null
- >(null);
-
- const loadDaemons = useCallback(async () => {
- try {
- setError(null);
- const response = await listDaemons();
- setDaemons(response.daemons);
- } catch (err) {
- setError(
- err instanceof Error ? err.message : "Failed to load daemons"
- );
- } finally {
- setLoading(false);
- }
- }, []);
-
- useEffect(() => {
- loadDaemons();
- }, [loadDaemons]);
-
- // Auto-refresh every 30 seconds
- useEffect(() => {
- const interval = setInterval(() => {
- loadDaemons();
- }, 30000);
- return () => clearInterval(interval);
- }, [loadDaemons]);
-
- const handleRestartDaemon = async (id: string) => {
- try {
- setRestartingDaemonId(id);
- setError(null);
- await restartDaemon(id);
- setRestartConfirmDaemonId(null);
- // Daemon will restart, so refresh the list after a short delay
- setTimeout(() => {
- loadDaemons();
- }, 2000);
- } catch (err) {
- setError(
- err instanceof Error ? err.message : "Failed to restart daemon"
- );
- } finally {
- setRestartingDaemonId(null);
- }
- };
-
- return (
- <section className="border border-[rgba(117,170,252,0.25)] bg-[#0d1b2d] p-4">
- <div className="flex items-center justify-between mb-3 pb-2 border-b border-[rgba(117,170,252,0.15)]">
- <div className="flex items-center gap-2">
- <h2 className="text-[11px] font-mono uppercase tracking-wide text-[#8899aa]">
- Connected Daemons
- </h2>
- {daemons.length > 0 && (
- <span className="text-[10px] font-mono text-[#556677]">
- ({daemons.filter((d) => d.status === "connected").length}{" "}
- connected / {daemons.length} total)
- </span>
- )}
- </div>
- <button
- onClick={loadDaemons}
- disabled={loading}
- className="text-[10px] font-mono text-[#75aafc] hover:text-[#9bc3ff] disabled:opacity-50 bg-transparent border-none cursor-pointer p-0"
- title="Refresh"
- >
- {loading ? "..." : "\u21BB"}
- </button>
- </div>
-
- {error && <ErrorAlert>{error}</ErrorAlert>}
-
- {loading && daemons.length === 0 ? (
- <p className="text-[#7788aa] font-mono text-xs">Loading...</p>
- ) : daemons.length === 0 ? (
- <div className="text-center py-6">
- <p className="text-[#7788aa] font-mono text-xs mb-2">
- No daemons connected
- </p>
- <p className="text-[#556677] font-mono text-[10px]">
- Follow the setup instructions above to connect a daemon
- </p>
- </div>
- ) : (
- <div className="space-y-2">
- {daemons.map((daemon) => (
- <div
- key={daemon.id}
- className="border border-[rgba(117,170,252,0.15)] bg-[#0a1525] p-3"
- >
- <div className="flex items-center justify-between mb-2">
- <span className="font-mono text-xs text-[#9bc3ff]">
- {daemon.hostname || "Unknown Host"}
- </span>
- <div className="flex items-center gap-2">
- <span
- className={`text-[10px] font-mono uppercase px-2 py-0.5 border ${
- daemon.status === "connected"
- ? "text-green-400 border-green-700/50 bg-green-900/20"
- : daemon.status === "unhealthy"
- ? "text-yellow-400 border-yellow-700/50 bg-yellow-900/20"
- : "text-[#8899aa] border-[rgba(117,170,252,0.25)]"
- }`}
- >
- {daemon.status}
- </span>
- </div>
- </div>
- <div className="font-mono text-[10px] text-[#7788aa] space-y-1">
- <div className="flex justify-between">
- <span>Tasks</span>
- <span className="text-[#9bc3ff]">
- {daemon.currentTaskCount} / {daemon.maxConcurrentTasks}
- </span>
- </div>
- <div className="flex justify-between">
- <span>Connected</span>
- <span className="text-[#75aafc]">
- {new Date(daemon.connectedAt).toLocaleString()}
- </span>
- </div>
- {daemon.machineId && (
- <div className="flex justify-between">
- <span>Machine</span>
- <span
- className="text-[#556677] truncate ml-2"
- title={daemon.machineId}
- >
- {daemon.machineId.substring(0, 16)}...
- </span>
- </div>
- )}
- </div>
- {/* Restart Section */}
- {daemon.status === "connected" && (
- <div className="mt-3 pt-2 border-t border-[rgba(117,170,252,0.1)]">
- {restartConfirmDaemonId === daemon.id ? (
- <div className="flex items-center justify-between gap-2">
- <span className="text-[10px] font-mono text-yellow-400">
- Restart daemon? Running tasks will be interrupted.
- </span>
- <div className="flex gap-2">
- <button
- onClick={() => setRestartConfirmDaemonId(null)}
- className="text-[10px] font-mono text-[#7788aa] hover:text-[#9bc3ff] px-2 py-1 bg-transparent border-none cursor-pointer"
- disabled={restartingDaemonId === daemon.id}
- >
- Cancel
- </button>
- <button
- onClick={() => handleRestartDaemon(daemon.id)}
- disabled={restartingDaemonId === daemon.id}
- className="text-[10px] font-mono text-red-400 hover:text-red-300 px-2 py-1 border border-red-700/50 bg-red-900/20 disabled:opacity-50 cursor-pointer"
- >
- {restartingDaemonId === daemon.id
- ? "Restarting..."
- : "Confirm"}
- </button>
- </div>
- </div>
- ) : (
- <button
- onClick={() => setRestartConfirmDaemonId(daemon.id)}
- className="text-[10px] font-mono text-[#75aafc] hover:text-[#9bc3ff] bg-transparent border-none cursor-pointer p-0"
- >
- &#x27F3; Restart Daemon
- </button>
- )}
- </div>
- )}
- </div>
- ))}
- </div>
- )}
- </section>
- );
-}
-
-// =============================================================================
-// Main Page
-// =============================================================================
-
-export default function DaemonPage() {
- const {
- isAuthenticated,
- isAuthConfigured,
- isLoading: authLoading,
- } = useAuth();
- const navigate = useNavigate();
-
- useEffect(() => {
- if (!authLoading && isAuthConfigured && !isAuthenticated) {
- navigate("/login");
- }
- }, [authLoading, isAuthConfigured, isAuthenticated, navigate]);
-
- if (authLoading) {
- return (
- <div className="relative z-10 min-h-screen flex flex-col bg-[#0a1628]">
- <Masthead showNav />
- <main className="flex-1 flex items-center justify-center">
- <p className="text-[#7788aa] font-mono text-sm">Loading...</p>
- </main>
- </div>
- );
- }
-
- return (
- <div className="relative z-10 min-h-screen flex flex-col bg-[#0a1628]">
- <Masthead showNav />
- <main className="flex-1 px-4 py-6 max-w-4xl mx-auto w-full">
- {/* Page header */}
- <div className="mb-6">
- <h1 className="text-sm font-mono uppercase tracking-wide text-[#9bc3ff] mb-1">
- Daemon Management
- </h1>
- <p className="text-[#556677] font-mono text-[10px]">
- Download, configure, and monitor Makima daemons
- </p>
- </div>
-
- <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
- {/* Left Column: Downloads & Setup */}
- <div className="space-y-6">
- <DownloadSection />
- <SetupSection />
- </div>
-
- {/* Right Column: Edge Deployment & Connected Daemons */}
- <div className="space-y-6">
- <ConnectedDaemonsSection />
- <CloudflareAgentSection />
- </div>
- </div>
- </main>
- </div>
- );
-}