-
-
Onboarding
-
Give us support for sandbox agent
-
- Before you keep going, give us support for sandbox agent and star the repo right here in the app.
+
+
Welcome to Foundry
+
Support Sandbox Agent
+
+ Star the repo to help us grow and stay up to date with new releases.
{starRepoError ? (
@@ -1062,18 +1081,20 @@ export function MockLayout({ workspaceId, selectedHandoffId, selectedSessionId }
) : null}
-
+
@@ -1112,8 +1135,9 @@ export function MockLayout({ workspaceId, selectedHandoffId, selectedSessionId }
onMarkUnread={markHandoffUnread}
onRenameHandoff={renameHandoff}
onRenameBranch={renameBranch}
+ onReorderProjects={reorderProjects}
/>
-
+
0 ? "#ff4f00" : "#444",
- color: "#fff",
+ background: viewModel.repos.length > 0 ? "rgba(255, 255, 255, 0.12)" : "#444",
+ color: "#e4e4e7",
cursor: viewModel.repos.length > 0 ? "pointer" : "not-allowed",
fontWeight: 600,
}}
@@ -1178,6 +1202,7 @@ export function MockLayout({ workspaceId, selectedHandoffId, selectedSessionId }
onMarkUnread={markHandoffUnread}
onRenameHandoff={renameHandoff}
onRenameBranch={renameBranch}
+ onReorderProjects={reorderProjects}
/>
- {isCopied ? "Copied" : "Copy"}
+ {isCopied ? "Copied" : null}
diff --git a/factory/packages/frontend/src/components/mock-layout/prompt-composer.tsx b/factory/packages/frontend/src/components/mock-layout/prompt-composer.tsx
index d036d03..970a94a 100644
--- a/factory/packages/frontend/src/components/mock-layout/prompt-composer.tsx
+++ b/factory/packages/frontend/src/components/mock-layout/prompt-composer.tsx
@@ -1,7 +1,7 @@
import { memo, type Ref } from "react";
import { useStyletron } from "baseui";
import { ChatComposer, type ChatComposerClassNames } from "@sandbox-agent/react";
-import { ArrowUpFromLine, FileCode, Square, X } from "lucide-react";
+import { FileCode, SendHorizonal, Square, X } from "lucide-react";
import { ModelPicker } from "./model-picker";
import { PROMPT_TEXTAREA_MAX_HEIGHT, PROMPT_TEXTAREA_MIN_HEIGHT } from "./ui";
@@ -45,7 +45,7 @@ export const PromptComposer = memo(function PromptComposer({
borderRadius: "16px",
minHeight: `${PROMPT_TEXTAREA_MIN_HEIGHT + 36}px`,
transition: "border-color 200ms ease",
- ":focus-within": { borderColor: "rgba(255, 255, 255, 0.3)" },
+ ":focus-within": { borderColor: "rgba(255, 255, 255, 0.15)" },
display: "flex",
flexDirection: "column",
}),
@@ -82,9 +82,9 @@ export const PromptComposer = memo(function PromptComposer({
justifyContent: "center",
color: theme.colors.contentPrimary,
transition: "background 200ms ease",
- backgroundColor: isRunning ? "rgba(255, 255, 255, 0.06)" : "#ff4f00",
+ backgroundColor: isRunning ? "rgba(255, 255, 255, 0.06)" : "rgba(255, 255, 255, 0.12)",
":hover": {
- backgroundColor: isRunning ? "rgba(255, 255, 255, 0.12)" : "#ff6a00",
+ backgroundColor: isRunning ? "rgba(255, 255, 255, 0.12)" : "rgba(255, 255, 255, 0.20)",
},
":disabled": {
cursor: "not-allowed",
@@ -157,7 +157,7 @@ export const PromptComposer = memo(function PromptComposer({
allowEmptySubmit={isRunning}
submitLabel={isRunning ? "Stop" : "Send"}
classNames={composerClassNames}
- renderSubmitContent={() => (isRunning ?
:
)}
+ renderSubmitContent={() => (isRunning ?
:
)}
renderFooter={() => (
-
+
+
{!isTerminal ? (
@@ -208,12 +208,14 @@ export const RightSidebar = memo(function RightSidebar({
) : null}
+
Changes
@@ -268,19 +272,20 @@ export const RightSidebar = memo(function RightSidebar({
all: "unset",
display: "flex",
alignItems: "center",
- height: "100%",
- padding: "0 16px",
+ padding: "4px 12px",
+ marginTop: "6px",
+ marginBottom: "6px",
+ borderRadius: "8px",
cursor: "pointer",
fontSize: "12px",
- fontWeight: 600,
+ fontWeight: 500,
whiteSpace: "nowrap",
color: rightTab === "files" ? theme.colors.contentPrimary : theme.colors.contentSecondary,
- borderBottom: `2px solid ${rightTab === "files" ? "#ff4f00" : "transparent"}`,
- marginBottom: "-1px",
- transitionProperty: "color, border-color",
+ backgroundColor: rightTab === "files" ? "rgba(255, 255, 255, 0.06)" : "transparent",
+ transitionProperty: "color, background-color",
transitionDuration: "200ms",
transitionTimingFunction: "ease",
- ":hover": { color: "#e4e4e7" },
+ ":hover": { color: "#e4e4e7", backgroundColor: rightTab === "files" ? "rgba(255, 255, 255, 0.06)" : "rgba(255, 255, 255, 0.04)" },
})}
>
All Files
@@ -360,6 +365,7 @@ export const RightSidebar = memo(function RightSidebar({
)}
+
{contextMenu.menu ?
: null}
);
diff --git a/factory/packages/frontend/src/components/mock-layout/sidebar.tsx b/factory/packages/frontend/src/components/mock-layout/sidebar.tsx
index 2141fea..4aa76a8 100644
--- a/factory/packages/frontend/src/components/mock-layout/sidebar.tsx
+++ b/factory/packages/frontend/src/components/mock-layout/sidebar.tsx
@@ -1,4 +1,4 @@
-import { memo, useState } from "react";
+import { memo, useRef, useState } from "react";
import { useStyletron } from "baseui";
import { LabelSmall, LabelXSmall } from "baseui/typography";
import { ChevronDown, ChevronUp, CloudUpload, GitPullRequestDraft, ListChecks, Plus } from "lucide-react";
@@ -30,6 +30,7 @@ export const Sidebar = memo(function Sidebar({
onMarkUnread,
onRenameHandoff,
onRenameBranch,
+ onReorderProjects,
}: {
projects: ProjectSection[];
activeId: string;
@@ -38,10 +39,13 @@ export const Sidebar = memo(function Sidebar({
onMarkUnread: (id: string) => void;
onRenameHandoff: (id: string) => void;
onRenameBranch: (id: string) => void;
+ onReorderProjects: (fromIndex: number, toIndex: number) => void;
}) {
const [css, theme] = useStyletron();
const contextMenu = useContextMenu();
const [collapsedProjects, setCollapsedProjects] = useState
>({});
+ const dragIndexRef = useRef(null);
+ const [dragOverIndex, setDragOverIndex] = useState(null);
return (
@@ -53,10 +57,10 @@ export const Sidebar = memo(function Sidebar({
display: none !important;
}
`}
-
+
Tasks
@@ -65,17 +69,17 @@ export const Sidebar = memo(function Sidebar({
onClick={onCreate}
className={css({
all: "unset",
- width: "24px",
- height: "24px",
- borderRadius: "4px",
- backgroundColor: "#ff4f00",
- color: "#ffffff",
+ width: "26px",
+ height: "26px",
+ borderRadius: "8px",
+ backgroundColor: "rgba(255, 255, 255, 0.12)",
+ color: "#e4e4e7",
cursor: "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center",
transition: "background 200ms ease",
- ":hover": { backgroundColor: "#ff6a00" },
+ ":hover": { backgroundColor: "rgba(255, 255, 255, 0.20)" },
})}
>
@@ -83,11 +87,48 @@ export const Sidebar = memo(function Sidebar({
- {projects.map((project) => {
+ {projects.map((project, projectIndex) => {
const isCollapsed = collapsedProjects[project.id] === true;
+ const isDragOver = dragOverIndex === projectIndex && dragIndexRef.current !== projectIndex;
return (
-
+
{
+ dragIndexRef.current = projectIndex;
+ event.dataTransfer.effectAllowed = "move";
+ event.dataTransfer.setData("text/plain", String(projectIndex));
+ }}
+ onDragOver={(event) => {
+ event.preventDefault();
+ event.dataTransfer.dropEffect = "move";
+ setDragOverIndex(projectIndex);
+ }}
+ onDragLeave={() => {
+ setDragOverIndex((current) => (current === projectIndex ? null : current));
+ }}
+ onDrop={(event) => {
+ event.preventDefault();
+ const fromIndex = dragIndexRef.current;
+ if (fromIndex != null && fromIndex !== projectIndex) {
+ onReorderProjects(fromIndex, projectIndex);
+ }
+ dragIndexRef.current = null;
+ setDragOverIndex(null);
+ }}
+ onDragEnd={() => {
+ dragIndexRef.current = null;
+ setDragOverIndex(null);
+ }}
+ className={css({
+ display: "flex",
+ flexDirection: "column",
+ gap: "4px",
+ borderTop: isDragOver ? "2px solid #ff4f00" : "2px solid transparent",
+ transition: "border-color 150ms ease",
+ })}
+ >
setCollapsedProjects((current) => ({
@@ -102,7 +143,7 @@ export const Sidebar = memo(function Sidebar({
justifyContent: "space-between",
padding: "10px 8px 4px",
gap: "8px",
- cursor: "pointer",
+ cursor: "grab",
userSelect: "none",
":hover": { opacity: 0.8 },
})}
@@ -150,9 +191,11 @@ export const Sidebar = memo(function Sidebar({
{project.label}
-
- {formatRelativeAge(project.updatedAtMs)}
-
+ {isCollapsed ? (
+
+ {formatRelativeAge(project.updatedAtMs)}
+
+ ) : null}
{!isCollapsed && project.handoffs.map((handoff) => {
@@ -177,7 +220,7 @@ export const Sidebar = memo(function Sidebar({
])
}
className={css({
- padding: "12px",
+ padding: "8px 12px",
borderRadius: "8px",
border: "1px solid transparent",
backgroundColor: isActive ? "rgba(255, 255, 255, 0.06)" : "transparent",
@@ -204,35 +247,17 @@ export const Sidebar = memo(function Sidebar({
{handoff.title}
- {hasDiffs ? (
-
- +{totalAdded}
- -{totalRemoved}
-
- ) : null}
-
-
-
- {handoff.repoName}
-
{handoff.pullRequest != null ? (
@@ -243,7 +268,13 @@ export const Sidebar = memo(function Sidebar({
) : (
)}
-
+ {hasDiffs ? (
+
+ +{totalAdded}
+ -{totalRemoved}
+
+ ) : null}
+
{formatRelativeAge(handoff.updatedAtMs)}
diff --git a/factory/packages/frontend/src/components/mock-layout/tab-strip.tsx b/factory/packages/frontend/src/components/mock-layout/tab-strip.tsx
index c540349..9f0cd50 100644
--- a/factory/packages/frontend/src/components/mock-layout/tab-strip.tsx
+++ b/factory/packages/frontend/src/components/mock-layout/tab-strip.tsx
@@ -42,12 +42,18 @@ export const TabStrip = memo(function TabStrip({
return (
<>
+
1 ? [{ label: "Close tab", onClick: () => onCloseTab(tab.id) }] : []),
])
}
+ data-tab
className={css({
display: "flex",
alignItems: "center",
gap: "6px",
- padding: "0 14px",
- borderBottom: isActive ? "2px solid #ff4f00" : "2px solid transparent",
+ padding: "4px 12px",
+ marginTop: "6px",
+ marginBottom: "6px",
+ borderRadius: "8px",
+ backgroundColor: isActive ? "rgba(255, 255, 255, 0.06)" : "transparent",
cursor: "pointer",
- transition: "color 200ms ease, border-color 200ms ease",
+ transition: "color 200ms ease, background-color 200ms ease",
flexShrink: 0,
- ":hover": { color: "#e4e4e7" },
+ ":hover": { color: "#e4e4e7", backgroundColor: isActive ? "rgba(255, 255, 255, 0.06)" : "rgba(255, 255, 255, 0.04)" },
})}
>
) : (
-
+
{tab.sessionName}
)}
@@ -138,7 +148,8 @@ export const TabStrip = memo(function TabStrip({
{
event.stopPropagation();
onCloseTab(tab.id);
@@ -161,29 +172,34 @@ export const TabStrip = memo(function TabStrip({
onCloseDiffTab(path);
}
}}
+ data-tab
className={css({
display: "flex",
alignItems: "center",
gap: "6px",
- padding: "0 14px",
- borderBottom: "2px solid transparent",
+ padding: "4px 12px",
+ marginTop: "6px",
+ marginBottom: "6px",
+ borderRadius: "8px",
+ backgroundColor: isActive ? "rgba(255, 255, 255, 0.06)" : "transparent",
cursor: "pointer",
- transition: "color 200ms ease, border-color 200ms ease",
+ transition: "color 200ms ease, background-color 200ms ease",
flexShrink: 0,
- ":hover": { color: "#e4e4e7" },
+ ":hover": { color: "#e4e4e7", backgroundColor: isActive ? "rgba(255, 255, 255, 0.06)" : "rgba(255, 255, 255, 0.04)" },
})}
>
{fileName(path)}
{
event.stopPropagation();
onCloseDiffTab(path);
diff --git a/factory/packages/frontend/src/components/mock-layout/transcript-header.tsx b/factory/packages/frontend/src/components/mock-layout/transcript-header.tsx
index 820cff6..56a9c76 100644
--- a/factory/packages/frontend/src/components/mock-layout/transcript-header.tsx
+++ b/factory/packages/frontend/src/components/mock-layout/transcript-header.tsx
@@ -30,7 +30,7 @@ export const TranscriptHeader = memo(function TranscriptHeader({
const [css, theme] = useStyletron();
return (
-
+
{editingField === "title" ? (
onStartEditingField("title", handoff.title)}
>
{handoff.title}
diff --git a/factory/packages/frontend/src/components/mock-layout/ui.tsx b/factory/packages/frontend/src/components/mock-layout/ui.tsx
index e517410..90d205c 100644
--- a/factory/packages/frontend/src/components/mock-layout/ui.tsx
+++ b/factory/packages/frontend/src/components/mock-layout/ui.tsx
@@ -178,8 +178,8 @@ export const Shell = styled("div", ({ $theme }) => ({
display: "grid",
gap: "1px",
height: "100dvh",
- backgroundColor: $theme.colors.borderOpaque,
- gridTemplateColumns: "280px minmax(0, 1fr) 380px",
+ backgroundColor: $theme.colors.backgroundSecondary,
+ gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1.5fr) 380px",
overflow: "hidden",
}));