-
Welcome to Foundry
+
+ Welcome to Foundry
+
Support Sandbox Agent
Star the repo to help us grow and stay up to date with new releases.
@@ -1127,17 +1231,20 @@ export function MockLayout({ workspaceId, selectedHandoffId, selectedSessionId }
return (
<>
-
-
+
+
+
+
+
-
+
+
+
+
{starRepoPrompt}
>
@@ -1194,41 +1304,49 @@ export function MockLayout({ workspaceId, selectedHandoffId, selectedSessionId }
return (
<>
-
- {
- setActiveTabIdByHandoff((current) => ({ ...current, [activeHandoff.id]: tabId }));
- }}
- onSetLastAgentTabId={(tabId) => {
- setLastAgentTabIdByHandoff((current) => ({ ...current, [activeHandoff.id]: tabId }));
- }}
- onSetOpenDiffs={(paths) => {
- setOpenDiffsByHandoff((current) => ({ ...current, [activeHandoff.id]: paths }));
- }}
- />
-
+
+
+
+
+
+ {
+ setActiveTabIdByHandoff((current) => ({ ...current, [activeHandoff.id]: tabId }));
+ }}
+ onSetLastAgentTabId={(tabId) => {
+ setLastAgentTabIdByHandoff((current) => ({ ...current, [activeHandoff.id]: tabId }));
+ }}
+ onSetOpenDiffs={(paths) => {
+ setOpenDiffsByHandoff((current) => ({ ...current, [activeHandoff.id]: paths }));
+ }}
+ />
+
+
+
+
+
{starRepoPrompt}
>
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 970a94a..98d5ad0 100644
--- a/factory/packages/frontend/src/components/mock-layout/prompt-composer.tsx
+++ b/factory/packages/frontend/src/components/mock-layout/prompt-composer.tsx
@@ -69,9 +69,14 @@ export const PromptComposer = memo(function PromptComposer({
"::placeholder": { color: theme.colors.contentSecondary },
}),
submit: css({
- all: "unset",
+ appearance: "none",
+ WebkitAppearance: "none",
+ boxSizing: "border-box",
width: "32px",
height: "32px",
+ padding: "0",
+ margin: "0",
+ border: "none",
borderRadius: "6px",
cursor: "pointer",
position: "absolute",
@@ -80,6 +85,8 @@ export const PromptComposer = memo(function PromptComposer({
display: "flex",
alignItems: "center",
justifyContent: "center",
+ lineHeight: 0,
+ fontSize: 0,
color: theme.colors.contentPrimary,
transition: "background 200ms ease",
backgroundColor: isRunning ? "rgba(255, 255, 255, 0.06)" : "rgba(255, 255, 255, 0.12)",
@@ -92,9 +99,12 @@ export const PromptComposer = memo(function PromptComposer({
},
}),
submitContent: css({
- display: "inline-flex",
+ display: "flex",
alignItems: "center",
justifyContent: "center",
+ width: "100%",
+ height: "100%",
+ lineHeight: 0,
color: isRunning ? theme.colors.contentPrimary : "#ffffff",
}),
};
@@ -157,15 +167,10 @@ export const PromptComposer = memo(function PromptComposer({
allowEmptySubmit={isRunning}
submitLabel={isRunning ? "Stop" : "Send"}
classNames={composerClassNames}
- renderSubmitContent={() => (isRunning ?
:
)}
+ renderSubmitContent={() => (isRunning ?
:
)}
renderFooter={() => (
-
+
)}
/>
diff --git a/factory/packages/frontend/src/components/mock-layout/right-sidebar.tsx b/factory/packages/frontend/src/components/mock-layout/right-sidebar.tsx
index 69d8717..1ac1092 100644
--- a/factory/packages/frontend/src/components/mock-layout/right-sidebar.tsx
+++ b/factory/packages/frontend/src/components/mock-layout/right-sidebar.tsx
@@ -151,220 +151,230 @@ export const RightSidebar = memo(function RightSidebar({
}}
className={css({
all: "unset",
- display: "flex",
+ boxSizing: "border-box",
+ display: "inline-flex",
alignItems: "center",
gap: "6px",
padding: "6px 12px",
borderRadius: "8px",
fontSize: "12px",
fontWeight: 500,
+ lineHeight: 1,
color: "#e4e4e7",
cursor: "pointer",
transition: "all 200ms ease",
":hover": { backgroundColor: "rgba(255, 255, 255, 0.06)", color: "#ffffff" },
})}
>
-
+
{pullRequestUrl ? "Open PR" : "Publish PR"}
- Push
+ Push
- Archive
+ Archive
) : null}
-
-
-
setRightTab("changes")}
+
+
- Changes
- {handoff.fileChanges.length > 0 ? (
-
- {handoff.fileChanges.length}
-
- ) : null}
-
- setRightTab("files")}
- className={css({
- all: "unset",
- display: "flex",
- alignItems: "center",
- padding: "4px 12px",
- marginTop: "6px",
- marginBottom: "6px",
- borderRadius: "8px",
- cursor: "pointer",
- fontSize: "12px",
- fontWeight: 500,
- whiteSpace: "nowrap",
- color: rightTab === "files" ? theme.colors.contentPrimary : theme.colors.contentSecondary,
- backgroundColor: rightTab === "files" ? "rgba(255, 255, 255, 0.06)" : "transparent",
- transitionProperty: "color, background-color",
- transitionDuration: "200ms",
- transitionTimingFunction: "ease",
- ":hover": { color: "#e4e4e7", backgroundColor: rightTab === "files" ? "rgba(255, 255, 255, 0.06)" : "rgba(255, 255, 255, 0.04)" },
- })}
- >
- All Files
-
-
-
-
- {rightTab === "changes" ? (
-
- {handoff.fileChanges.length === 0 ? (
-
- No changes yet
-
+
setRightTab("changes")}
+ className={css({
+ all: "unset",
+ boxSizing: "border-box",
+ display: "inline-flex",
+ alignItems: "center",
+ gap: "6px",
+ padding: "4px 12px",
+ marginTop: "6px",
+ marginBottom: "6px",
+ marginLeft: "6px",
+ borderRadius: "8px",
+ cursor: "pointer",
+ fontSize: "12px",
+ fontWeight: 500,
+ lineHeight: 1,
+ whiteSpace: "nowrap",
+ color: rightTab === "changes" ? theme.colors.contentPrimary : theme.colors.contentSecondary,
+ backgroundColor: rightTab === "changes" ? "rgba(255, 255, 255, 0.06)" : "transparent",
+ transitionProperty: "color, background-color",
+ transitionDuration: "200ms",
+ transitionTimingFunction: "ease",
+ ":hover": { color: "#e4e4e7", backgroundColor: rightTab === "changes" ? "rgba(255, 255, 255, 0.06)" : "rgba(255, 255, 255, 0.04)" },
+ })}
+ >
+ Changes
+ {handoff.fileChanges.length > 0 ? (
+
+ {handoff.fileChanges.length}
+
) : null}
- {handoff.fileChanges.map((file) => {
- const isActive = activeTabId === diffTabId(file.path);
- const TypeIcon = file.type === "A" ? FilePlus : file.type === "D" ? FileX : FileCode;
- const iconColor = file.type === "A" ? "#7ee787" : file.type === "D" ? "#ffa198" : theme.colors.contentTertiary;
- return (
- onOpenDiff(file.path)}
- onContextMenu={(event) => openFileMenu(event, file.path)}
- className={css({
- display: "flex",
- alignItems: "center",
- gap: "8px",
- padding: "6px 10px",
- borderRadius: "6px",
- backgroundColor: isActive ? "rgba(255, 255, 255, 0.06)" : "transparent",
- cursor: "pointer",
- ":hover": { backgroundColor: "rgba(255, 255, 255, 0.06)" },
- })}
- >
-
-
- {file.path}
-
+
+
setRightTab("files")}
+ className={css({
+ all: "unset",
+ boxSizing: "border-box",
+ display: "inline-flex",
+ alignItems: "center",
+ padding: "4px 12px",
+ marginTop: "6px",
+ marginBottom: "6px",
+ borderRadius: "8px",
+ cursor: "pointer",
+ fontSize: "12px",
+ fontWeight: 500,
+ lineHeight: 1,
+ whiteSpace: "nowrap",
+ color: rightTab === "files" ? theme.colors.contentPrimary : theme.colors.contentSecondary,
+ backgroundColor: rightTab === "files" ? "rgba(255, 255, 255, 0.06)" : "transparent",
+ transitionProperty: "color, background-color",
+ transitionDuration: "200ms",
+ transitionTimingFunction: "ease",
+ ":hover": { color: "#e4e4e7", backgroundColor: rightTab === "files" ? "rgba(255, 255, 255, 0.06)" : "rgba(255, 255, 255, 0.04)" },
+ })}
+ >
+ All Files
+
+
+
+
+ {rightTab === "changes" ? (
+
+ {handoff.fileChanges.length === 0 ? (
+
+ No changes yet
+
+ ) : null}
+ {handoff.fileChanges.map((file) => {
+ const isActive = activeTabId === diffTabId(file.path);
+ const TypeIcon = file.type === "A" ? FilePlus : file.type === "D" ? FileX : FileCode;
+ const iconColor = file.type === "A" ? "#7ee787" : file.type === "D" ? "#ffa198" : theme.colors.contentTertiary;
+ return (
onOpenDiff(file.path)}
+ onContextMenu={(event) => openFileMenu(event, file.path)}
className={css({
display: "flex",
alignItems: "center",
- gap: "6px",
- flexShrink: 0,
- fontSize: "11px",
- fontFamily: '"IBM Plex Mono", monospace',
+ gap: "8px",
+ padding: "6px 10px",
+ borderRadius: "6px",
+ backgroundColor: isActive ? "rgba(255, 255, 255, 0.06)" : "transparent",
+ cursor: "pointer",
+ ":hover": { backgroundColor: "rgba(255, 255, 255, 0.06)" },
})}
>
-
+{file.added}
-
-{file.removed}
-
{file.type}
+
+
+ {file.path}
+
+
+ +{file.added}
+ -{file.removed}
+ {file.type}
+
+ );
+ })}
+
+ ) : (
+
+ {handoff.fileTree.length > 0 ? (
+
+ ) : (
+
+ No files yet
- );
- })}
-
- ) : (
-
- {handoff.fileTree.length > 0 ? (
-
- ) : (
-
- No files yet
-
- )}
-
- )}
-
+ )}
+
+ )}
+
{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 4aa76a8..4cac6a3 100644
--- a/factory/packages/frontend/src/components/mock-layout/sidebar.tsx
+++ b/factory/packages/frontend/src/components/mock-layout/sidebar.tsx
@@ -60,15 +60,19 @@ export const Sidebar = memo(function Sidebar({
Tasks
- {
+ if (event.key === "Enter" || event.key === " ") onCreate();
+ }}
className={css({
- all: "unset",
width: "26px",
height: "26px",
borderRadius: "8px",
@@ -79,11 +83,12 @@ export const Sidebar = memo(function Sidebar({
alignItems: "center",
justifyContent: "center",
transition: "background 200ms ease",
+ flexShrink: 0,
":hover": { backgroundColor: "rgba(255, 255, 255, 0.20)" },
})}
>
-
-
+
+
@@ -191,97 +196,93 @@ export const Sidebar = memo(function Sidebar({
{project.label}
- {isCollapsed ? (
-
- {formatRelativeAge(project.updatedAtMs)}
-
- ) : null}
+ {isCollapsed ? {formatRelativeAge(project.updatedAtMs)} : null}
- {!isCollapsed && project.handoffs.map((handoff) => {
- const isActive = handoff.id === activeId;
- const isDim = handoff.status === "archived";
- const isRunning = handoff.tabs.some((tab) => tab.status === "running");
- const hasUnread = handoff.tabs.some((tab) => tab.unread);
- const isDraft = handoff.pullRequest == null || handoff.pullRequest.status === "draft";
- const totalAdded = handoff.fileChanges.reduce((sum, file) => sum + file.added, 0);
- const totalRemoved = handoff.fileChanges.reduce((sum, file) => sum + file.removed, 0);
- const hasDiffs = totalAdded > 0 || totalRemoved > 0;
+ {!isCollapsed &&
+ project.handoffs.map((handoff) => {
+ const isActive = handoff.id === activeId;
+ const isDim = handoff.status === "archived";
+ const isRunning = handoff.tabs.some((tab) => tab.status === "running");
+ const hasUnread = handoff.tabs.some((tab) => tab.unread);
+ const isDraft = handoff.pullRequest == null || handoff.pullRequest.status === "draft";
+ const totalAdded = handoff.fileChanges.reduce((sum, file) => sum + file.added, 0);
+ const totalRemoved = handoff.fileChanges.reduce((sum, file) => sum + file.removed, 0);
+ const hasDiffs = totalAdded > 0 || totalRemoved > 0;
- return (
- onSelect(handoff.id)}
- onContextMenu={(event) =>
- contextMenu.open(event, [
- { label: "Rename task", onClick: () => onRenameHandoff(handoff.id) },
- { label: "Rename branch", onClick: () => onRenameBranch(handoff.id) },
- { label: "Mark as unread", onClick: () => onMarkUnread(handoff.id) },
- ])
- }
- className={css({
- padding: "8px 12px",
- borderRadius: "8px",
- border: "1px solid transparent",
- backgroundColor: isActive ? "rgba(255, 255, 255, 0.06)" : "transparent",
- cursor: "pointer",
- transition: "all 200ms ease",
- ":hover": {
- backgroundColor: "rgba(255, 255, 255, 0.06)",
- },
- })}
- >
-
-
-
-
-
- {handoff.title}
-
- {handoff.pullRequest != null ? (
-
-
- #{handoff.pullRequest.number}
-
- {handoff.pullRequest.status === "draft" ? : null}
-
- ) : (
-
- )}
- {hasDiffs ? (
-
-
+{totalAdded}
-
-{totalRemoved}
+ return (
+
onSelect(handoff.id)}
+ onContextMenu={(event) =>
+ contextMenu.open(event, [
+ { label: "Rename task", onClick: () => onRenameHandoff(handoff.id) },
+ { label: "Rename branch", onClick: () => onRenameBranch(handoff.id) },
+ { label: "Mark as unread", onClick: () => onMarkUnread(handoff.id) },
+ ])
+ }
+ className={css({
+ padding: "8px 12px",
+ borderRadius: "8px",
+ border: "1px solid transparent",
+ backgroundColor: isActive ? "rgba(255, 255, 255, 0.06)" : "transparent",
+ cursor: "pointer",
+ transition: "all 200ms ease",
+ ":hover": {
+ backgroundColor: "rgba(255, 255, 255, 0.06)",
+ },
+ })}
+ >
+
+
+
- ) : null}
-
- {formatRelativeAge(handoff.updatedAtMs)}
-
+
+ {handoff.title}
+
+ {handoff.pullRequest != null ? (
+
+
+ #{handoff.pullRequest.number}
+
+ {handoff.pullRequest.status === "draft" ? : null}
+
+ ) : (
+
+ )}
+ {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 9f0cd50..03f05ed 100644
--- a/factory/packages/frontend/src/components/mock-layout/tab-strip.tsx
+++ b/factory/packages/frontend/src/components/mock-layout/tab-strip.tsx
@@ -216,6 +216,7 @@ export const TabStrip = memo(function TabStrip({
padding: "0 10px",
cursor: "pointer",
opacity: 0.4,
+ lineHeight: 0,
":hover": { opacity: 0.7 },
flexShrink: 0,
})}
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 56a9c76..1107324 100644
--- a/factory/packages/frontend/src/components/mock-layout/transcript-header.tsx
+++ b/factory/packages/frontend/src/components/mock-layout/transcript-header.tsx
@@ -115,7 +115,7 @@ export const TranscriptHeader = memo(function TranscriptHeader({
-
+
847 min used
{activeTab ? (
@@ -136,20 +137,22 @@ export const TranscriptHeader = memo(function TranscriptHeader({
onClick={() => onSetActiveTabUnread(!activeTab.unread)}
className={css({
all: "unset",
- display: "flex",
+ boxSizing: "border-box",
+ display: "inline-flex",
alignItems: "center",
gap: "5px",
padding: "4px 10px",
borderRadius: "6px",
fontSize: "11px",
fontWeight: 500,
+ lineHeight: 1,
color: theme.colors.contentSecondary,
cursor: "pointer",
transition: "all 200ms ease",
":hover": { backgroundColor: "rgba(255, 255, 255, 0.06)", color: theme.colors.contentPrimary },
})}
>
-
{activeTab.unread ? "Mark read" : "Mark unread"}
+
{activeTab.unread ? "Mark read" : "Mark unread"}
) : null}
diff --git a/factory/packages/frontend/src/components/mock-layout/ui.tsx b/factory/packages/frontend/src/components/mock-layout/ui.tsx
index 90d205c..3ebb00e 100644
--- a/factory/packages/frontend/src/components/mock-layout/ui.tsx
+++ b/factory/packages/frontend/src/components/mock-layout/ui.tsx
@@ -175,16 +175,15 @@ export const TabAvatar = memo(function TabAvatar({ tab }: { tab: AgentTab }) {
});
export const Shell = styled("div", ({ $theme }) => ({
- display: "grid",
- gap: "1px",
+ display: "flex",
height: "100dvh",
backgroundColor: $theme.colors.backgroundSecondary,
- gridTemplateColumns: "minmax(0, 1fr) minmax(0, 1.5fr) 380px",
overflow: "hidden",
}));
export const SPanel = styled("section", ({ $theme }) => ({
minHeight: 0,
+ flex: 1,
display: "flex",
flexDirection: "column" as const,
backgroundColor: $theme.colors.backgroundSecondary,