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
20 changes: 7 additions & 13 deletions landing/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState, useEffect, useCallback, useRef } from "react";
import type React from "react";
import { useState, useEffect, useCallback, useRef } from "react";
import { Diagram } from "./components/Diagram";
import { SlackPanel } from "./components/SlackPanel";
import { StepIndicator } from "./components/StepIndicator";
Expand Down Expand Up @@ -56,7 +57,7 @@ const App: React.FC = () => {
setCurrentStepIndex(index);
setIsPlaying(false);
},
[clearTimer],
[clearTimer]
);

const handleTogglePlay = useCallback(() => {
Expand All @@ -76,7 +77,7 @@ const App: React.FC = () => {
setCurrentStepIndex(0);
setIsPlaying(true);
},
[clearTimer],
[clearTimer]
);

const currentStep = steps[currentStepIndex];
Expand All @@ -93,8 +94,7 @@ const App: React.FC = () => {
width: 32,
height: 32,
borderRadius: 8,
background:
"linear-gradient(135deg, #10B981 0%, #059669 100%)",
background: "linear-gradient(135deg, #10B981 0%, #059669 100%)",
display: "flex",
alignItems: "center",
justifyContent: "center",
Expand Down Expand Up @@ -170,19 +170,13 @@ const App: React.FC = () => {

{/* SVG Diagram */}
<div style={{ flex: 1, padding: "0 16px" }}>
<Diagram
currentStep={currentStep}
prompt={selectedPrompt}
/>
<Diagram currentStep={currentStep} prompt={selectedPrompt} />
</div>
</div>

{/* Slack chat panel */}
<div style={layout.slackPanel}>
<SlackPanel
steps={steps}
currentStepIndex={currentStepIndex}
/>
<SlackPanel steps={steps} currentStepIndex={currentStepIndex} />
</div>
</div>

Expand Down
2 changes: 1 addition & 1 deletion landing/src/components/AnimatedPacket.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import type React from "react";
import { motion } from "framer-motion";
import { NODES } from "../nodes";

Expand Down
2 changes: 1 addition & 1 deletion landing/src/components/ConnectionLine.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import type React from "react";
import { colors } from "../styles";
import { NODES } from "../nodes";

Expand Down
6 changes: 4 additions & 2 deletions landing/src/components/Diagram.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import type React from "react";
import { AnimatePresence } from "framer-motion";
import { NODES, CONNECTIONS } from "../nodes";
import type { FlowStep, PromptOption } from "../types";
Expand Down Expand Up @@ -49,10 +49,12 @@ export const Diagram: React.FC<DiagramProps> = ({ currentStep, prompt }) => {
viewBox="0 0 800 540"
preserveAspectRatio="xMidYMid meet"
style={{ display: "block" }}
aria-hidden="true"
>
{/* Defs for gradients */}
<defs>
<linearGradient id="lineGrad" x1="0%" y1="0%" x2="100%" y2="0%">
{/* biome-ignore lint/correctness/useUniqueElementIds: SVG gradient ID is scoped within this component's SVG element */}
<linearGradient id="diagram-lineGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="rgba(74, 158, 255, 0.1)" />
<stop offset="50%" stopColor="rgba(74, 158, 255, 0.3)" />
<stop offset="100%" stopColor="rgba(74, 158, 255, 0.1)" />
Expand Down
9 changes: 2 additions & 7 deletions landing/src/components/DiagramNode.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import type React from "react";
import { motion } from "framer-motion";
import type { NodePosition } from "../types";
import { NodeIcon } from "./NodeIcon";
Expand Down Expand Up @@ -67,12 +67,7 @@ export const DiagramNode: React.FC<DiagramNodeProps> = ({
/>

{/* Icon */}
<foreignObject
x={node.x - 14}
y={nodeY + 10}
width={28}
height={28}
>
<foreignObject x={node.x - 14} y={nodeY + 10} width={28} height={28}>
<div
style={{
color: isActive ? colors.accent : colors.textSecondary,
Expand Down
50 changes: 43 additions & 7 deletions landing/src/components/NodeIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import type React from "react";

interface NodeIconProps {
type: string;
Expand All @@ -12,7 +12,13 @@ export const NodeIcon: React.FC<NodeIconProps> = ({ type, size = 28 }) => {
switch (type) {
case "slack":
return (
<svg width={s} height={s} viewBox="0 0 24 24" fill="none">
<svg
width={s}
height={s}
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
>
<path
d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zm1.271 0a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313z"
fill="#E01E5A"
Expand All @@ -34,7 +40,13 @@ export const NodeIcon: React.FC<NodeIconProps> = ({ type, size = 28 }) => {

case "gateway":
return (
<svg width={s} height={s} viewBox="0 0 24 24" fill="none">
<svg
width={s}
height={s}
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
>
<rect
x="2"
y="3"
Expand Down Expand Up @@ -79,7 +91,13 @@ export const NodeIcon: React.FC<NodeIconProps> = ({ type, size = 28 }) => {

case "sandbox":
return (
<svg width={s} height={s} viewBox="0 0 24 24" fill="none">
<svg
width={s}
height={s}
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
>
<rect
x="3"
y="3"
Expand Down Expand Up @@ -110,7 +128,13 @@ export const NodeIcon: React.FC<NodeIconProps> = ({ type, size = 28 }) => {

case "mcp":
return (
<svg width={s} height={s} viewBox="0 0 24 24" fill="none">
<svg
width={s}
height={s}
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
>
<circle cx="12" cy="12" r="9" stroke={color} strokeWidth="1.5" />
<path
d="M12 3c-3 3-3 6 0 9s3 6 0 9"
Expand All @@ -134,7 +158,13 @@ export const NodeIcon: React.FC<NodeIconProps> = ({ type, size = 28 }) => {

case "llm":
return (
<svg width={s} height={s} viewBox="0 0 24 24" fill="none">
<svg
width={s}
height={s}
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
>
<path
d="M12 3L20 7.5V16.5L12 21L4 16.5V7.5L12 3Z"
stroke={color}
Expand All @@ -152,7 +182,13 @@ export const NodeIcon: React.FC<NodeIconProps> = ({ type, size = 28 }) => {

default:
return (
<svg width={s} height={s} viewBox="0 0 24 24" fill="none">
<svg
width={s}
height={s}
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
>
<circle cx="12" cy="12" r="9" stroke={color} strokeWidth="1.5" />
</svg>
);
Expand Down
2 changes: 1 addition & 1 deletion landing/src/components/PromptSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import type React from "react";
import { motion } from "framer-motion";
import type { PromptOption } from "../types";
import { colors } from "../styles";
Expand Down
10 changes: 6 additions & 4 deletions landing/src/components/SlackPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useEffect, useState, useRef } from "react";
import type React from "react";
import { useEffect, useState, useRef } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { colors } from "../styles";
import type { FlowStep } from "../types";
Expand Down Expand Up @@ -74,6 +75,7 @@ export const SlackPanel: React.FC<SlackPanelProps> = ({
}
}, [currentStepIndex, steps]);

// biome-ignore lint/correctness/useExhaustiveDependencies: scrollRef is a stable ref and does not need to be a dependency
useEffect(() => {
if (scrollRef.current) {
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
Expand Down Expand Up @@ -307,9 +309,7 @@ const PermissionMessage: React.FC<{ text: string }> = ({ text }) => (
>
Permission Required
</div>
<div
style={{ fontSize: 13, color: colors.text, marginBottom: 10 }}
>
<div style={{ fontSize: 13, color: colors.text, marginBottom: 10 }}>
{text}
</div>
<div style={{ display: "flex", gap: 8 }}>
Expand All @@ -330,6 +330,7 @@ const PermissionMessage: React.FC<{ text: string }> = ({ text }) => (
Allow for 1 hour
</motion.button>
<button
type="button"
style={{
background: "transparent",
color: colors.textSecondary,
Expand All @@ -352,6 +353,7 @@ function renderMarkdownLight(text: string): React.ReactNode {
return parts.map((part, i) => {
if (part.startsWith("**") && part.endsWith("**")) {
return (
// biome-ignore lint/suspicious/noArrayIndexKey: markdown parts are derived from static text and never reordered
<strong key={i} style={{ fontWeight: 600 }}>
{part.slice(2, -2)}
</strong>
Expand Down
20 changes: 17 additions & 3 deletions landing/src/components/StepIndicator.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import type React from "react";
import { motion } from "framer-motion";
import type { FlowStep } from "../types";
import { colors } from "../styles";
Expand Down Expand Up @@ -50,7 +50,13 @@ export const StepIndicator: React.FC<StepIndicatorProps> = ({
}}
>
{isPlaying ? (
<svg width="14" height="14" viewBox="0 0 14 14" fill="white">
<svg
width="14"
height="14"
viewBox="0 0 14 14"
fill="white"
aria-hidden="true"
>
<rect x="2" y="1" width="3.5" height="12" rx="1" />
<rect x="8.5" y="1" width="3.5" height="12" rx="1" />
</svg>
Expand All @@ -60,6 +66,7 @@ export const StepIndicator: React.FC<StepIndicatorProps> = ({
height="14"
viewBox="0 0 14 14"
fill="white"
aria-hidden="true"
>
<path d="M3 1.5L12 7L3 12.5V1.5Z" />
</svg>
Expand All @@ -85,7 +92,13 @@ export const StepIndicator: React.FC<StepIndicatorProps> = ({
color: colors.textSecondary,
}}
>
<svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor">
<svg
width="14"
height="14"
viewBox="0 0 14 14"
fill="currentColor"
aria-hidden="true"
>
<path d="M7 1a6 6 0 0 0-6 6h2a4 4 0 0 1 7.17-2.42L8.5 6H13V1.5l-1.76 1.76A5.98 5.98 0 0 0 7 1zM1 7v4.5l1.76-1.76A5.98 5.98 0 0 0 13 7h-2a4 4 0 0 1-7.17 2.42L5.5 8H1z" />
</svg>
</motion.button>
Expand All @@ -102,6 +115,7 @@ export const StepIndicator: React.FC<StepIndicatorProps> = ({
>
{steps.map((_, index) => (
<motion.button
// biome-ignore lint/suspicious/noArrayIndexKey: steps array is static and never reordered
key={index}
onClick={() => onStepClick(index)}
style={{
Expand Down
2 changes: 1 addition & 1 deletion landing/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import App from "./App";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
</React.StrictMode>
);
12 changes: 7 additions & 5 deletions landing/src/steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ export function buildSteps(prompt: PromptOption): FlowStep[] {
{
id: "check-sandbox",
title: "Check sandbox status",
description: "Gateway checks if a sandbox is already running for this user",
description:
"Gateway checks if a sandbox is already running for this user",
activeNodes: ["gateway"],
callout: {
node: "gateway",
Expand All @@ -62,7 +63,8 @@ export function buildSteps(prompt: PromptOption): FlowStep[] {
{
id: "check-snapshot",
title: "Look up snapshot",
description: "Gateway checks if a pre-built snapshot exists for the agent",
description:
"Gateway checks if a pre-built snapshot exists for the agent",
activeNodes: ["gateway"],
callout: {
node: "gateway",
Expand Down Expand Up @@ -110,8 +112,7 @@ export function buildSteps(prompt: PromptOption): FlowStep[] {
{
id: "deliver-message",
title: "Deliver prompt to sandbox",
description:
"Gateway sends the user's message to the sandbox runtime",
description: "Gateway sends the user's message to the sandbox runtime",
activeNodes: ["gateway", "sandbox"],
packet: {
from: "gateway",
Expand Down Expand Up @@ -312,7 +313,8 @@ export function buildSteps(prompt: PromptOption): FlowStep[] {
{
id: "done",
title: "Complete",
description: "User sees the response in Slack — all processing happened in an isolated sandbox",
description:
"User sees the response in Slack — all processing happened in an isolated sandbox",
activeNodes: ["slack"],
slackEvent: {
type: "bot",
Expand Down
Loading