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
2 changes: 0 additions & 2 deletions apps/array/src/renderer/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { CursorGlow } from "@components/CursorGlow";
import { LoginTransition } from "@components/LoginTransition";
import { MainLayout } from "@components/MainLayout";
import { AuthScreen } from "@features/auth/components/AuthScreen";
Expand Down Expand Up @@ -67,7 +66,6 @@ function App() {

return (
<>
<CursorGlow />
<AnimatePresence mode="wait">
{!isAuthenticated ? (
<motion.div
Expand Down
127 changes: 127 additions & 0 deletions apps/array/src/renderer/components/TorchGlow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { useThemeStore } from "@stores/themeStore";
import { useEffect, useState } from "react";

interface TorchGlowProps {
containerRef: React.RefObject<HTMLElement>;
}

export function TorchGlow({ containerRef }: TorchGlowProps) {
const isDarkMode = useThemeStore((state) => state.isDarkMode);
const [mousePos, setMousePos] = useState<{ x: number; y: number } | null>(
null,
);
const [isHovering, setIsHovering] = useState(false);

useEffect(() => {
const container = containerRef.current;
if (!container) return;

const handleMouseMove = (e: MouseEvent) => {
const rect = container.getBoundingClientRect();
setMousePos({
x: e.clientX - rect.left,
y: e.clientY - rect.top,
});
};

const handleMouseEnter = () => setIsHovering(true);
const handleMouseLeave = () => {
setIsHovering(false);
setMousePos(null);
};

container.addEventListener("mousemove", handleMouseMove);
container.addEventListener("mouseenter", handleMouseEnter);
container.addEventListener("mouseleave", handleMouseLeave);

return () => {
container.removeEventListener("mousemove", handleMouseMove);
container.removeEventListener("mouseenter", handleMouseEnter);
container.removeEventListener("mouseleave", handleMouseLeave);
};
}, [containerRef]);

// Only show in dark mode when hovering
if (!isDarkMode || !isHovering || !mousePos) return null;

return (
<>
{/* SVG filter for grainy torch light texture */}
<svg
aria-hidden="true"
style={{
position: "absolute",
width: 0,
height: 0,
overflow: "hidden",
}}
>
<defs>
<filter id="torch-grain" x="-50%" y="-50%" width="200%" height="200%">
<feTurbulence
type="fractalNoise"
baseFrequency="2.5"
numOctaves="4"
result="noise"
/>
<feColorMatrix
type="matrix"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.6 0"
result="alphaNoise"
/>
<feComposite in="SourceGraphic" in2="alphaNoise" operator="in" />
</filter>
</defs>
</svg>

{/* Base layer - outer glow */}
<div
style={{
position: "absolute",
left: mousePos.x - 75,
top: mousePos.y - 80,
width: 150,
height: 160,
pointerEvents: "none",
background:
"radial-gradient(ellipse 60% 70% at 50% 55%, rgba(255,120,50,0.58) 0%, transparent 70%)",
filter: "url(#torch-grain)",
zIndex: 1,
}}
/>

{/* Middle layer - offset for irregular shape */}
<div
style={{
position: "absolute",
left: mousePos.x - 65,
top: mousePos.y - 70,
width: 140,
height: 130,
pointerEvents: "none",
background:
"radial-gradient(ellipse 70% 55% at 45% 50%, rgba(255,100,40,0.42) 0%, transparent 65%)",
filter: "url(#torch-grain)",
zIndex: 1,
}}
/>

{/* Inner layer - pulsing flame core */}
<div
className="torch-glow-pulse"
style={{
position: "absolute",
left: mousePos.x - 45,
top: mousePos.y - 55,
width: 90,
height: 100,
pointerEvents: "none",
background:
"radial-gradient(ellipse 50% 60% at 50% 45%, rgba(255,180,80,0.48) 0%, transparent 70%)",
filter: "url(#torch-grain)",
zIndex: 1,
}}
/>
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { TorchGlow } from "@components/TorchGlow";
import { FolderPicker } from "@features/folder-picker/components/FolderPicker";
import type { MessageEditorHandle } from "@features/message-editor/components/MessageEditor";
import type { ExecutionMode } from "@features/sessions/stores/sessionStore";
Expand Down Expand Up @@ -35,6 +36,7 @@ export function TaskInput() {
const { lastUsedLocalWorkspaceMode } = useSettingsStore();

const editorRef = useRef<MessageEditorHandle>(null);
const containerRef = useRef<HTMLDivElement>(null);

const [selectedDirectory, setSelectedDirectory] = useState(
lastUsedDirectory || "",
Expand Down Expand Up @@ -81,89 +83,100 @@ export function TaskInput() {
});

return (
<Flex
align="center"
justify="center"
height="100%"
style={{ position: "relative" }}
<div
ref={containerRef}
style={{
position: "relative",
height: "100%",
width: "100%",
overflow: "hidden",
}}
>
<svg
aria-hidden="true"
style={{
position: "absolute",
bottom: 0,
left: 0,
width: "100%",
height: "100.333%",
pointerEvents: "none",
opacity: 0.4,
maskImage: "linear-gradient(to top, black 0%, transparent 100%)",
WebkitMaskImage:
"linear-gradient(to top, black 0%, transparent 100%)",
}}
>
<defs>
<pattern
id="dot-pattern"
patternUnits="userSpaceOnUse"
width="8"
height="8"
>
<circle cx="0" cy="0" r="1" fill={DOT_FILL} />
<circle cx="0" cy="8" r="1" fill={DOT_FILL} />
<circle cx="8" cy="8" r="1" fill={DOT_FILL} />
<circle cx="8" cy="0" r="1" fill={DOT_FILL} />
<circle cx="4" cy="4" r="1" fill={DOT_FILL} />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#dot-pattern)" />
</svg>
<TorchGlow containerRef={containerRef} />
<Flex
direction="column"
gap="4"
style={{
fontFamily: "monospace",
width: "100%",
maxWidth: "600px",
position: "relative",
zIndex: 1,
}}
align="center"
justify="center"
height="100%"
style={{ position: "relative" }}
>
<Flex gap="2" align="center">
<FolderPicker
value={selectedDirectory}
onChange={handleDirectoryChange}
placeholder="Select working directory..."
size="1"
/>
{selectedDirectory && (
<BranchSelect
value={selectedBranch}
onChange={setSelectedBranch}
directoryPath={selectedDirectory}
runMode={runMode}
<svg
aria-hidden="true"
style={{
position: "absolute",
bottom: 0,
left: 0,
width: "100%",
height: "100.333%",
pointerEvents: "none",
opacity: 0.4,
maskImage: "linear-gradient(to top, black 0%, transparent 100%)",
WebkitMaskImage:
"linear-gradient(to top, black 0%, transparent 100%)",
}}
>
<defs>
<pattern
id="dot-pattern"
patternUnits="userSpaceOnUse"
width="8"
height="8"
>
<circle cx="0" cy="0" r="1" fill={DOT_FILL} />
<circle cx="0" cy="8" r="1" fill={DOT_FILL} />
<circle cx="8" cy="8" r="1" fill={DOT_FILL} />
<circle cx="8" cy="0" r="1" fill={DOT_FILL} />
<circle cx="4" cy="4" r="1" fill={DOT_FILL} />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#dot-pattern)" />
</svg>
<Flex
direction="column"
gap="4"
style={{
fontFamily: "monospace",
width: "100%",
maxWidth: "600px",
position: "relative",
zIndex: 1,
}}
>
<Flex gap="2" align="center">
<FolderPicker
value={selectedDirectory}
onChange={handleDirectoryChange}
placeholder="Select working directory..."
size="1"
/>
)}
</Flex>
{selectedDirectory && (
<BranchSelect
value={selectedBranch}
onChange={setSelectedBranch}
directoryPath={selectedDirectory}
runMode={runMode}
/>
)}
</Flex>

<TaskInputEditor
ref={editorRef}
sessionId="task-input"
repoPath={selectedDirectory}
isCreatingTask={isCreatingTask}
runMode={runMode}
localWorkspaceMode={localWorkspaceMode}
onLocalWorkspaceModeChange={setLocalWorkspaceMode}
canSubmit={canSubmit}
onSubmit={handleSubmit}
hasDirectory={!!selectedDirectory}
onEmptyChange={setEditorIsEmpty}
executionMode={executionMode}
onModeChange={handleModeChange}
/>

<TaskInputEditor
ref={editorRef}
sessionId="task-input"
repoPath={selectedDirectory}
isCreatingTask={isCreatingTask}
runMode={runMode}
localWorkspaceMode={localWorkspaceMode}
onLocalWorkspaceModeChange={setLocalWorkspaceMode}
canSubmit={canSubmit}
onSubmit={handleSubmit}
hasDirectory={!!selectedDirectory}
onEmptyChange={setEditorIsEmpty}
executionMode={executionMode}
onModeChange={handleModeChange}
/>

<SuggestedTasks editorRef={editorRef} />
<SuggestedTasks editorRef={editorRef} />
</Flex>
</Flex>
</Flex>
</div>
);
}
15 changes: 15 additions & 0 deletions apps/array/src/renderer/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,21 @@
animation: campfire-pulse 1s ease-in-out infinite;
}

/* Torch glow pulse animation for cursor flame effect */
@keyframes torch-pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.3;
}
}

.torch-glow-pulse {
animation: torch-pulse 1.5s ease-in-out infinite;
}

.radix-themes {
/* Font families */
--default-font-family:
Expand Down
Loading