diff --git a/apps/desktop/src/renderer/index.html b/apps/desktop/src/renderer/index.html
index fbfd3b0b1..470bf7d96 100644
--- a/apps/desktop/src/renderer/index.html
+++ b/apps/desktop/src/renderer/index.html
@@ -1,16 +1,12 @@
-
+
+
Multica
-
-
-
-
+
+
diff --git a/apps/desktop/src/renderer/src/App.tsx b/apps/desktop/src/renderer/src/App.tsx
index cd1be2e4a..6711b81bd 100644
--- a/apps/desktop/src/renderer/src/App.tsx
+++ b/apps/desktop/src/renderer/src/App.tsx
@@ -1,17 +1,16 @@
-function App(): React.JSX.Element {
+import { RouterProvider } from "react-router-dom";
+import { ThemeProvider } from "./components/theme-provider";
+import { Toaster } from "@multica/ui/components/ui/sonner";
+import { QueryProvider } from "@multica/core/provider";
+import { router } from "./router";
+
+export default function App() {
return (
-
-
Multica Desktop
-
+
+
+
+
+
+
);
}
-
-export default App;
diff --git a/apps/desktop/src/renderer/src/globals.css b/apps/desktop/src/renderer/src/globals.css
new file mode 100644
index 000000000..ebd01f84a
--- /dev/null
+++ b/apps/desktop/src/renderer/src/globals.css
@@ -0,0 +1,28 @@
+@import "tailwindcss";
+@import "tw-animate-css";
+@import "@multica/ui/styles/tokens.css";
+
+@custom-variant dark (&:is(.dark *));
+
+@source "../../../../packages/ui/**/*.tsx";
+@source "../../../../packages/core/**/*.tsx";
+@source "../../../../packages/views/**/*.tsx";
+@source "./**/*.tsx";
+
+@layer base {
+ * {
+ @apply border-border outline-ring/50;
+ scrollbar-width: thin;
+ scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
+ }
+ *::-webkit-scrollbar { width: 6px; height: 6px; }
+ *::-webkit-scrollbar-track { background: var(--scrollbar-track); }
+ *::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 3px; }
+ *::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); }
+ body {
+ @apply bg-background text-foreground;
+ }
+ html {
+ @apply font-sans;
+ }
+}
diff --git a/apps/desktop/src/renderer/src/main.tsx b/apps/desktop/src/renderer/src/main.tsx
index f8fc6f511..16e637c66 100644
--- a/apps/desktop/src/renderer/src/main.tsx
+++ b/apps/desktop/src/renderer/src/main.tsx
@@ -1,9 +1,5 @@
-import { StrictMode } from "react";
-import { createRoot } from "react-dom/client";
+import ReactDOM from "react-dom/client";
import App from "./App";
+import "./globals.css";
-createRoot(document.getElementById("root")!).render(
-
-
- ,
-);
+ReactDOM.createRoot(document.getElementById("root")!).render();
diff --git a/apps/desktop/src/renderer/src/pages/issue-detail-page.tsx b/apps/desktop/src/renderer/src/pages/issue-detail-page.tsx
new file mode 100644
index 000000000..0c2e82219
--- /dev/null
+++ b/apps/desktop/src/renderer/src/pages/issue-detail-page.tsx
@@ -0,0 +1,8 @@
+import { useParams } from "react-router-dom";
+import { IssueDetail } from "@multica/views/issues/components";
+
+export function IssueDetailPage() {
+ const { id } = useParams<{ id: string }>();
+ if (!id) return null;
+ return ;
+}
diff --git a/apps/desktop/src/renderer/src/pages/login.tsx b/apps/desktop/src/renderer/src/pages/login.tsx
new file mode 100644
index 000000000..bf0bfefd5
--- /dev/null
+++ b/apps/desktop/src/renderer/src/pages/login.tsx
@@ -0,0 +1,139 @@
+import { useState, useCallback } from "react";
+import { useNavigate } from "react-router-dom";
+import { useAuthStore } from "@/platform/auth";
+import { useWorkspaceStore } from "@/platform/workspace";
+import { api } from "@/platform/api";
+import {
+ Card,
+ CardHeader,
+ CardTitle,
+ CardDescription,
+ CardContent,
+} from "@multica/ui/components/ui/card";
+import { Input } from "@multica/ui/components/ui/input";
+import { Button } from "@multica/ui/components/ui/button";
+import { Label } from "@multica/ui/components/ui/label";
+import { MulticaIcon } from "../components/multica-icon";
+import { TitleBar } from "../components/title-bar";
+
+export function LoginPage() {
+ const navigate = useNavigate();
+ const [step, setStep] = useState<"email" | "code">("email");
+ const [email, setEmail] = useState("");
+ const [code, setCode] = useState("");
+ const [error, setError] = useState("");
+ const [loading, setLoading] = useState(false);
+
+ const handleSendCode = useCallback(async () => {
+ setLoading(true);
+ setError("");
+ try {
+ await useAuthStore.getState().sendCode(email);
+ setStep("code");
+ } catch {
+ setError("Failed to send code");
+ } finally {
+ setLoading(false);
+ }
+ }, [email]);
+
+ const handleVerify = useCallback(async () => {
+ setLoading(true);
+ setError("");
+ try {
+ await useAuthStore.getState().verifyCode(email, code);
+ const wsList = await api.listWorkspaces();
+ useWorkspaceStore.getState().hydrateWorkspace(wsList);
+ navigate("/issues", { replace: true });
+ } catch {
+ setError("Invalid code");
+ } finally {
+ setLoading(false);
+ }
+ }, [email, code, navigate]);
+
+ return (
+
+
+
+
+
+
+
+
+ Sign in to Multica
+
+ {step === "email"
+ ? "Enter your email to get a login code"
+ : `We sent a code to ${email}`}
+
+
+
+ {step === "email" ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/src/pages/placeholder.tsx b/apps/desktop/src/renderer/src/pages/placeholder.tsx
new file mode 100644
index 000000000..d746debde
--- /dev/null
+++ b/apps/desktop/src/renderer/src/pages/placeholder.tsx
@@ -0,0 +1,12 @@
+export function PlaceholderPage({ title }: { title: string }) {
+ return (
+
+
+
{title}
+
+ Coming soon — requires page extraction to @multica/views.
+
+
+
+ );
+}
diff --git a/apps/desktop/src/renderer/src/router.tsx b/apps/desktop/src/renderer/src/router.tsx
new file mode 100644
index 000000000..81ea6e4c4
--- /dev/null
+++ b/apps/desktop/src/renderer/src/router.tsx
@@ -0,0 +1,31 @@
+import { createHashRouter, Navigate } from "react-router-dom";
+import { DashboardShell } from "./components/dashboard-shell";
+import { LoginPage } from "./pages/login";
+import { IssueDetailPage } from "./pages/issue-detail-page";
+import { PlaceholderPage } from "./pages/placeholder";
+
+// Extracted pages from @multica/views
+import { IssuesPage } from "@multica/views/issues/components";
+import { MyIssuesPage } from "@multica/views/my-issues";
+import { RuntimesPage } from "@multica/views/runtimes";
+import { SkillsPage } from "@multica/views/skills";
+
+export const router = createHashRouter([
+ {
+ path: "/",
+ element: ,
+ children: [
+ { index: true, element: },
+ { path: "issues", element: },
+ { path: "issues/:id", element: },
+ { path: "my-issues", element: },
+ { path: "runtimes", element: },
+ { path: "skills", element: },
+ { path: "agents", element: },
+ { path: "inbox", element: },
+ { path: "settings", element: },
+ { path: "board", element: },
+ ],
+ },
+ { path: "/login", element: },
+]);