diff --git a/apps/array/package.json b/apps/array/package.json
index bc00d5de..013266a2 100644
--- a/apps/array/package.json
+++ b/apps/array/package.json
@@ -96,6 +96,7 @@
"@parcel/watcher": "^2.5.1",
"@phosphor-icons/react": "^2.1.10",
"@posthog/agent": "workspace:*",
+ "@posthog/electron-trpc": "workspace:*",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/themes": "^3.2.1",
@@ -125,6 +126,7 @@
"electron-log": "^5.4.3",
"electron-store": "^11.0.0",
"file-icon": "^6.0.0",
+ "framer-motion": "^12.26.2",
"idb-keyval": "^6.2.2",
"immer": "^11.0.1",
"inversify": "^7.10.6",
@@ -146,7 +148,6 @@
"remark-gfm": "^4.0.1",
"sonner": "^2.0.7",
"tippy.js": "^6.3.7",
- "@posthog/electron-trpc": "workspace:*",
"uuid": "^9.0.1",
"vscode-icons-js": "^11.6.1",
"zod": "^4.1.12",
diff --git a/apps/array/src/renderer/App.tsx b/apps/array/src/renderer/App.tsx
index f69e1c6a..18141560 100644
--- a/apps/array/src/renderer/App.tsx
+++ b/apps/array/src/renderer/App.tsx
@@ -1,3 +1,5 @@
+import { CursorGlow } from "@components/CursorGlow";
+import { LoginTransition } from "@components/LoginTransition";
import { MainLayout } from "@components/MainLayout";
import { AuthScreen } from "@features/auth/components/AuthScreen";
import { useAuthStore } from "@features/auth/stores/authStore";
@@ -6,11 +8,14 @@ import { initializePostHog } from "@renderer/lib/analytics";
import { initializeConnectivityStore } from "@renderer/stores/connectivityStore";
import { trpcVanilla } from "@renderer/trpc/client";
import { toast } from "@utils/toast";
-import { useEffect, useState } from "react";
+import { AnimatePresence, motion } from "framer-motion";
+import { useEffect, useRef, useState } from "react";
function App() {
const { isAuthenticated, initializeOAuth } = useAuthStore();
const [isLoading, setIsLoading] = useState(true);
+ const [showTransition, setShowTransition] = useState(false);
+ const wasAuthenticated = useRef(isAuthenticated);
// Initialize PostHog analytics
useEffect(() => {
@@ -36,6 +41,19 @@ function App() {
initializeOAuth().finally(() => setIsLoading(false));
}, [initializeOAuth]);
+ // Handle auth state change for transition
+ useEffect(() => {
+ if (!wasAuthenticated.current && isAuthenticated) {
+ // User just logged in - trigger transition
+ setShowTransition(true);
+ }
+ wasAuthenticated.current = isAuthenticated;
+ }, [isAuthenticated]);
+
+ const handleTransitionComplete = () => {
+ setShowTransition(false);
+ };
+
if (isLoading) {
return (
@@ -47,7 +65,36 @@ function App() {
);
}
- return isAuthenticated ? : ;
+ return (
+ <>
+
+
+ {!isAuthenticated ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+
+ >
+ );
}
export default App;
diff --git a/apps/array/src/renderer/assets/fonts/Halfre.otf b/apps/array/src/renderer/assets/fonts/Halfre.otf
new file mode 100644
index 00000000..5f5c8e62
Binary files /dev/null and b/apps/array/src/renderer/assets/fonts/Halfre.otf differ
diff --git a/apps/array/src/renderer/assets/images/cave-hero.jpg b/apps/array/src/renderer/assets/images/cave-hero.jpg
new file mode 100644
index 00000000..05e58d26
Binary files /dev/null and b/apps/array/src/renderer/assets/images/cave-hero.jpg differ
diff --git a/apps/array/src/renderer/assets/images/twig-logo.svg b/apps/array/src/renderer/assets/images/twig-logo.svg
new file mode 100644
index 00000000..f14796aa
--- /dev/null
+++ b/apps/array/src/renderer/assets/images/twig-logo.svg
@@ -0,0 +1,4 @@
+
diff --git a/apps/array/src/renderer/components/CampfireToggle.tsx b/apps/array/src/renderer/components/CampfireToggle.tsx
new file mode 100644
index 00000000..77f758a5
--- /dev/null
+++ b/apps/array/src/renderer/components/CampfireToggle.tsx
@@ -0,0 +1,22 @@
+import { Campfire } from "@phosphor-icons/react";
+import { IconButton, Tooltip } from "@radix-ui/themes";
+import { useThemeStore } from "@stores/themeStore";
+
+export function CampfireToggle() {
+ const { isDarkMode, toggleDarkMode } = useThemeStore();
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/apps/array/src/renderer/components/CursorGlow.tsx b/apps/array/src/renderer/components/CursorGlow.tsx
new file mode 100644
index 00000000..9a125343
--- /dev/null
+++ b/apps/array/src/renderer/components/CursorGlow.tsx
@@ -0,0 +1,37 @@
+import { useThemeStore } from "@stores/themeStore";
+import { useEffect, useState } from "react";
+
+export function CursorGlow() {
+ const isDarkMode = useThemeStore((state) => state.isDarkMode);
+ const [mousePos, setMousePos] = useState<{ x: number; y: number } | null>(
+ null,
+ );
+
+ useEffect(() => {
+ const handleMouseMove = (e: MouseEvent) => {
+ setMousePos({ x: e.clientX, y: e.clientY });
+ };
+
+ window.addEventListener("mousemove", handleMouseMove);
+ return () => window.removeEventListener("mousemove", handleMouseMove);
+ }, []);
+
+ // Only show in dark mode
+ if (!isDarkMode || !mousePos) return null;
+
+ return (
+
+ );
+}
diff --git a/apps/array/src/renderer/components/LoginTransition.tsx b/apps/array/src/renderer/components/LoginTransition.tsx
new file mode 100644
index 00000000..b56cdaec
--- /dev/null
+++ b/apps/array/src/renderer/components/LoginTransition.tsx
@@ -0,0 +1,28 @@
+import { motion } from "framer-motion";
+
+interface LoginTransitionProps {
+ isAnimating: boolean;
+ onComplete: () => void;
+}
+
+export function LoginTransition({
+ isAnimating,
+ onComplete,
+}: LoginTransitionProps) {
+ if (!isAnimating) return null;
+
+ return (
+
+ );
+}
diff --git a/apps/array/src/renderer/components/StatusBar.tsx b/apps/array/src/renderer/components/StatusBar.tsx
index 172e6d6e..4286dd0c 100644
--- a/apps/array/src/renderer/components/StatusBar.tsx
+++ b/apps/array/src/renderer/components/StatusBar.tsx
@@ -1,3 +1,4 @@
+import { CampfireToggle } from "@components/CampfireToggle";
import { StatusBarMenu } from "@components/StatusBarMenu";
import { Badge, Box, Code, Flex, Kbd } from "@radix-ui/themes";
import { useStatusBarStore } from "@stores/statusBarStore";
@@ -43,6 +44,7 @@ export function StatusBar({ showKeyHints = true }: StatusBarProps) {
)}
+
{IS_DEV && (
diff --git a/apps/array/src/renderer/features/auth/components/AuthScreen.tsx b/apps/array/src/renderer/features/auth/components/AuthScreen.tsx
index 29959879..794bf1da 100644
--- a/apps/array/src/renderer/features/auth/components/AuthScreen.tsx
+++ b/apps/array/src/renderer/features/auth/components/AuthScreen.tsx
@@ -1,17 +1,7 @@
-import { AsciiArt } from "@components/AsciiArt";
import { useAuthStore } from "@features/auth/stores/authStore";
-import {
- Box,
- Button,
- Callout,
- Card,
- Container,
- Flex,
- Heading,
- Select,
- Spinner,
- Text,
-} from "@radix-ui/themes";
+import { Box, Callout, Flex, Select, Spinner, Text } from "@radix-ui/themes";
+import caveHero from "@renderer/assets/images/cave-hero.jpg";
+import twigLogo from "@renderer/assets/images/twig-logo.svg";
import { trpcVanilla } from "@renderer/trpc/client";
import type { CloudRegion } from "@shared/types/oauth";
import { useMutation } from "@tanstack/react-query";
@@ -61,86 +51,114 @@ export function AuthScreen() {
};
return (
-
- {/* Left pane - Auth form */}
-
-
-
-
-
-
- Welcome to Array
-
- Sign in with your PostHog account
-
-
-
-
-
-
- PostHog region
-
-
-
-
- US Cloud
- EU Cloud
- {IS_DEV && (
- Development
- )}
-
-
-
-
- {authMutation.isError && (
-
-
- {getErrorMessage(authMutation.error)}
-
-
- )}
-
- {authMutation.isPending && (
-
-
- Waiting for authorization in your browser...
-
-
- )}
-
-
-
-
-
+
+ {/* Full-screen cave painting background */}
+
+
+ {/* Left side - login form */}
+
+
+
+
+
+ the dawn of a new agentic era
+
-
-
- {/* Right pane - ASCII Art */}
-
-
-
+
+
+
+ PostHog region
+
+
+
+
+ US Cloud
+ EU Cloud
+ {IS_DEV && Development}
+
+
+
+
+ {authMutation.isError && (
+
+
+ {getErrorMessage(authMutation.error)}
+
+
+ )}
+
+ {authMutation.isPending && (
+
+
+ Waiting for authorization in your browser...
+
+
+ )}
+
+
+
+
+
+
+ {/* Right side - empty, shows background */}
+
);
}
diff --git a/apps/array/src/renderer/features/sessions/components/GeneratingIndicator.tsx b/apps/array/src/renderer/features/sessions/components/GeneratingIndicator.tsx
index 956bc67e..375fe79c 100644
--- a/apps/array/src/renderer/features/sessions/components/GeneratingIndicator.tsx
+++ b/apps/array/src/renderer/features/sessions/components/GeneratingIndicator.tsx
@@ -1,7 +1,9 @@
-import { DotsCircleSpinner } from "@components/DotsCircleSpinner";
+import { Campfire } from "@phosphor-icons/react";
import { Flex, Text } from "@radix-ui/themes";
import { useEffect, useState } from "react";
+const ACTIVITIES = ["Foraging", "Hunting", "Building", "Gathering", "Crafting"];
+
export function formatDuration(ms: number): string {
const totalSeconds = Math.floor(ms / 1000);
const mins = Math.floor(totalSeconds / 60);
@@ -16,6 +18,7 @@ export function formatDuration(ms: number): string {
export function GeneratingIndicator() {
const [elapsed, setElapsed] = useState(0);
+ const [activityIndex, setActivityIndex] = useState(0);
useEffect(() => {
const startTime = Date.now();
@@ -26,6 +29,14 @@ export function GeneratingIndicator() {
return () => clearInterval(interval);
}, []);
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setActivityIndex((i) => (i + 1) % ACTIVITIES.length);
+ }, 2000);
+
+ return () => clearInterval(interval);
+ }, []);
+
return (
-
-
- Generating
-
-
+
+ {ACTIVITIES[activityIndex]}...
=6'}
- bun-types@1.3.5:
- resolution: {integrity: sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw==}
+ bun-types@1.3.6:
+ resolution: {integrity: sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ==}
bundle-require@5.1.0:
resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}
@@ -4709,6 +4712,20 @@ packages:
fraction.js@5.3.4:
resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
+ framer-motion@12.26.2:
+ resolution: {integrity: sha512-lflOQEdjquUi9sCg5Y1LrsZDlsjrHw7m0T9Yedvnk7Bnhqfkc89/Uha10J3CFhkL+TCZVCRw9eUGyM/lyYhXQA==}
+ peerDependencies:
+ '@emotion/is-prop-valid': '*'
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@emotion/is-prop-valid':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+
fresh@2.0.0:
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
engines: {node: '>= 0.8'}
@@ -5669,6 +5686,12 @@ packages:
mlly@1.8.0:
resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
+ motion-dom@12.26.2:
+ resolution: {integrity: sha512-KLMT1BroY8oKNeliA3JMNJ+nbCIsTKg6hJpDb4jtRAJ7nCKnnpg/LTq/NGqG90Limitz3kdAnAVXecdFVGlWTw==}
+
+ motion-utils@12.24.10:
+ resolution: {integrity: sha512-x5TFgkCIP4pPsRLpKoI86jv/q8t8FQOiM/0E8QKBzfMozWHfkKap2gA1hOki+B5g3IsBNpxbUnfOum1+dgvYww==}
+
mprocs@0.7.3:
resolution: {integrity: sha512-I1Ptja/UnNVVSV5QEnBdiiwW70HMWJlmnMq6nvw4J7fwZ3s8UZGuf/FG+FTAUr6CLfat8kriHj+mWLx7xsLKSw==}
engines: {node: '>=0.10.0'}
@@ -10892,9 +10915,9 @@ snapshots:
dependencies:
'@babel/types': 7.28.5
- '@types/bun@1.3.5':
+ '@types/bun@1.3.6':
dependencies:
- bun-types: 1.3.5
+ bun-types: 1.3.6
'@types/cacheable-request@6.0.3':
dependencies:
@@ -11537,7 +11560,7 @@ snapshots:
builtin-modules@3.3.0: {}
- bun-types@1.3.5:
+ bun-types@1.3.6:
dependencies:
'@types/node': 20.19.25
@@ -12387,6 +12410,15 @@ snapshots:
fraction.js@5.3.4: {}
+ framer-motion@12.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ motion-dom: 12.26.2
+ motion-utils: 12.24.10
+ tslib: 2.8.1
+ optionalDependencies:
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+
fresh@2.0.0: {}
fs-extra@10.1.0:
@@ -13645,6 +13677,12 @@ snapshots:
pkg-types: 1.3.1
ufo: 1.6.1
+ motion-dom@12.26.2:
+ dependencies:
+ motion-utils: 12.24.10
+
+ motion-utils@12.24.10: {}
+
mprocs@0.7.3: {}
mri@1.2.0: {}