diff --git a/app/(navigation)/(code)/assets/e2b.svg b/app/(navigation)/(code)/assets/e2b.svg
new file mode 100644
index 00000000..7cf3df4b
--- /dev/null
+++ b/app/(navigation)/(code)/assets/e2b.svg
@@ -0,0 +1,6 @@
+
diff --git a/app/(navigation)/(code)/assets/e2b/mask-group.png b/app/(navigation)/(code)/assets/e2b/mask-group.png
new file mode 100644
index 00000000..4a0cbd89
Binary files /dev/null and b/app/(navigation)/(code)/assets/e2b/mask-group.png differ
diff --git a/app/(navigation)/(code)/components/Frame.tsx b/app/(navigation)/(code)/components/Frame.tsx
index 35fdcc87..c5d6b53f 100644
--- a/app/(navigation)/(code)/components/Frame.tsx
+++ b/app/(navigation)/(code)/components/Frame.tsx
@@ -11,6 +11,7 @@ import ClerkFrame from "./frames/ClerkFrame";
import CloudflareFrame from "./frames/CloudflareFrame";
import Auth0Frame from "./frames/Auth0Frame";
import DefaultFrame from "./frames/DefaultFrame";
+import E2bFrame from "./frames/E2bFrame";
import ElevenLabsFrame from "./frames/ElevenLabsFrame";
import FirecrawlFrame from "./frames/FirecrawlFrame";
import GeminiFrame from "./frames/GeminiFrame";
@@ -56,6 +57,8 @@ const Frame = ({ resize = true }: { resize?: boolean }) => {
return ;
case THEMES.elevenlabs.id:
return ;
+ case THEMES.e2b.id:
+ return ;
case THEMES.resend.id:
return ;
case THEMES.browserbase.id:
diff --git a/app/(navigation)/(code)/components/frames/E2bFrame.module.css b/app/(navigation)/(code)/components/frames/E2bFrame.module.css
new file mode 100644
index 00000000..19d9343d
--- /dev/null
+++ b/app/(navigation)/(code)/components/frames/E2bFrame.module.css
@@ -0,0 +1,118 @@
+.frame {
+ --frame-background: #121212;
+ --selection-background: rgba(219, 68, 90, 0.4);
+ --selection-text-color: #ffffff;
+ --frame-header-background: #0a0a0a;
+ --frame-border: hsla(0, 0%, 100%, 0.1);
+ --frame-border-radius: 8px;
+ --frame-text-color: hsl(0, 0%, 98%);
+
+ &.withBackground {
+ position: relative;
+ overflow: hidden;
+ background-color: #ff3001;
+ }
+
+ &::selection {
+ background: var(--selection-background);
+ color: var(--selection-text-color);
+ }
+
+ &.darkMode {
+ --frame-background: #121212;
+ --frame-header-background: #0a0a0a;
+ --frame-border: hsla(0, 0%, 100%, 0.1);
+ --frame-text-color: hsl(0, 0%, 98%);
+ --selection-background: rgba(219, 68, 90, 0.4);
+ --selection-text-color: #ffffff;
+ }
+}
+
+/* Real element instead of ::after so html-to-image inlines the image on export */
+.background {
+ position: absolute;
+ z-index: 0;
+ top: -5%;
+ right: -5%;
+ width: 82%;
+ height: 82%;
+ background-image: url("../../assets/e2b/mask-group.png");
+ background-position: top right;
+ background-repeat: no-repeat;
+ background-size: contain;
+ mix-blend-mode: multiply;
+ pointer-events: none;
+}
+
+.window {
+ position: relative;
+ z-index: 1;
+ border: 0.5px solid var(--frame-border);
+ border-radius: var(--frame-border-radius);
+ -webkit-backdrop-filter: blur(3px);
+ backdrop-filter: blur(3px);
+ background: var(--frame-background);
+}
+
+.header {
+ display: flex;
+ height: 40px;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 16px;
+ border-bottom: 1px solid var(--frame-border);
+ background: var(--frame-header-background);
+ border-top-left-radius: calc(var(--frame-border-radius) - 0.5px);
+ border-top-right-radius: calc(var(--frame-border-radius) - 0.5px);
+ gap: 12px;
+}
+
+.fileName {
+ position: relative;
+ display: grid;
+ height: 40px;
+ align-items: center;
+ color: var(--frame-text-color);
+ font-size: 14px;
+ line-height: 40px;
+ vertical-align: top;
+
+ input,
+ & > span,
+ &:after {
+ position: static;
+ z-index: 2;
+ width: auto;
+ min-width: 1em;
+ padding: 0;
+ border: none;
+ margin: 0;
+ appearance: none;
+ background: none;
+ font: inherit;
+ grid-area: 1 / 2;
+ resize: none;
+ text-align: left;
+ }
+
+ & > span {
+ position: relative;
+ z-index: 1;
+ }
+
+ input {
+ color: inherit;
+ }
+
+ &:after {
+ content: attr(data-value) " ";
+ visibility: hidden;
+ }
+}
+
+.language {
+ height: 40px;
+ color: #898989;
+ font-size: 14px;
+ line-height: 40px;
+}
diff --git a/app/(navigation)/(code)/components/frames/E2bFrame.tsx b/app/(navigation)/(code)/components/frames/E2bFrame.tsx
new file mode 100644
index 00000000..05375d42
--- /dev/null
+++ b/app/(navigation)/(code)/components/frames/E2bFrame.tsx
@@ -0,0 +1,53 @@
+import classNames from "classnames";
+import { useAtom, useAtomValue } from "jotai";
+
+import { fileNameAtom, showBackgroundAtom } from "../../store";
+import { selectedLanguageAtom } from "../../store/code";
+import { paddingAtom } from "../../store/padding";
+import { themeDarkModeAtom } from "../../store/themes";
+
+import Editor from "../Editor";
+import sharedStyles from "./DefaultFrame.module.css";
+import styles from "./E2bFrame.module.css";
+
+const E2bFrame = () => {
+ const darkMode = useAtomValue(themeDarkModeAtom);
+ const [padding] = useAtom(paddingAtom);
+ const [showBackground] = useAtom(showBackgroundAtom);
+ const [fileName, setFileName] = useAtom(fileNameAtom);
+ const selectedLanguage = useAtomValue(selectedLanguageAtom);
+
+ return (
+
+ {!showBackground &&
}
+ {showBackground &&
}
+
+
+
+ setFileName(event.target.value)}
+ spellCheck={false}
+ tabIndex={-1}
+ size={1}
+ />
+ {fileName.length === 0 ? Untitled-1 : null}
+
+
{selectedLanguage?.name}
+
+
+
+
+ );
+};
+
+export default E2bFrame;
diff --git a/app/(navigation)/(code)/store/themes.ts b/app/(navigation)/(code)/store/themes.ts
index acb7137c..0fabdaed 100644
--- a/app/(navigation)/(code)/store/themes.ts
+++ b/app/(navigation)/(code)/store/themes.ts
@@ -33,6 +33,8 @@ import ElevenLabsLogo from "../assets/elevenlabs.svg";
import ElevenLabsLogoUrl from "../assets/elevenlabs.svg?url";
import ResendLogo from "../assets/resend.svg";
import ResendLogoUrl from "../assets/resend.svg?url";
+import E2bLogo from "../assets/e2b.svg";
+import E2bLogoUrl from "../assets/e2b.svg?url";
import BrowserbaseLogo from "../assets/browserbase.svg";
import BrowserbaseLogoUrl from "../assets/browserbase.svg?url";
import NuxtLogo from "../assets/nuxt.svg";
@@ -556,6 +558,41 @@ export const THEMES: { [index: string]: Theme } = {
}),
},
},
+ e2b: {
+ id: "e2b",
+ name: "E2B",
+ background: {
+ from: "#ff3001",
+ to: "#ff3001",
+ },
+ icon: E2bLogo,
+ iconUrl: E2bLogoUrl,
+ font: "commit-mono",
+ partner: true,
+ lineNumbers: true,
+ syntax: {
+ dark: convertToShikiTheme({
+ foreground: "#ffffff",
+ comment: "#585858",
+ keyword: "#DB445A",
+ constant: "#FFF",
+ parameter: "#ffffff",
+ property: "#ffffff",
+ function: "#FDB44D",
+ number: "#FDB44D",
+ objectLiteral: "#ffffff",
+ link: "#ffffff",
+ string: "#FF752F",
+ stringExpression: "#FF752F",
+ punctuation: "#ffffff",
+ highlight: "rgba(219,68,90, 0.4)",
+ highlightHover: "rgba(219,68,90, 0.2)",
+ highlightBorder: "#DB445A",
+ diffInserted: "#ffffff",
+ diffDeleted: "#DB445A",
+ }),
+ },
+ },
resend: {
id: "resend",
name: "Resend",