Skip to content

Commit 3001f55

Browse files
committed
runtime icons
1 parent fcd467e commit 3001f55

File tree

6 files changed

+236
-25
lines changed

6 files changed

+236
-25
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
export function BunLogoIcon({ className }: { className?: string }) {
2+
return (
3+
<svg className={className} viewBox="0 0 80 70" xmlns="http://www.w3.org/2000/svg">
4+
<path
5+
id="Shadow"
6+
d="M71.09,20.74c-.16-.17-.33-.34-.5-.5s-.33-.34-.5-.5-.33-.34-.5-.5-.33-.34-.5-.5-.33-.34-.5-.5-.33-.34-.5-.5-.33-.34-.5-.5A26.46,26.46,0,0,1,75.5,35.7c0,16.57-16.82,30.05-37.5,30.05-11.58,0-21.94-4.23-28.83-10.86l.5.5.5.5.5.5.5.5.5.5.5.5.5.5C19.55,65.3,30.14,69.75,42,69.75c20.68,0,37.5-13.48,37.5-30C79.5,32.69,76.46,26,71.09,20.74Z"
7+
/>
8+
<g id="Body">
9+
<path
10+
id="Background"
11+
d="M73,35.7c0,15.21-15.67,27.54-35,27.54S3,50.91,3,35.7C3,26.27,9,17.94,18.22,13S33.18,3,38,3s8.94,4.13,19.78,10C67,17.94,73,26.27,73,35.7Z"
12+
style={{ fill: "#fbf0df" }}
13+
/>
14+
<path
15+
id="Bottom_Shadow"
16+
data-name="Bottom Shadow"
17+
d="M73,35.7a21.67,21.67,0,0,0-.8-5.78c-2.73,33.3-43.35,34.9-59.32,24.94A40,40,0,0,0,38,63.24C57.3,63.24,73,50.89,73,35.7Z"
18+
style={{ fill: "#f6dece" }}
19+
/>
20+
<path
21+
id="Light_Shine"
22+
data-name="Light Shine"
23+
d="M24.53,11.17C29,8.49,34.94,3.46,40.78,3.45A9.29,9.29,0,0,0,38,3c-2.42,0-5,1.25-8.25,3.13-1.13.66-2.3,1.39-3.54,2.15-2.33,1.44-5,3.07-8,4.7C8.69,18.13,3,26.62,3,35.7c0,.4,0,.8,0,1.19C9.06,15.48,20.07,13.85,24.53,11.17Z"
24+
style={{ fill: "#fffefc" }}
25+
/>
26+
<path
27+
id="Top"
28+
d="M35.12,5.53A16.41,16.41,0,0,1,29.49,18c-.28.25-.06.73.3.59,3.37-1.31,7.92-5.23,6-13.14C35.71,5,35.12,5.12,35.12,5.53Zm2.27,0A16.24,16.24,0,0,1,39,19c-.12.35.31.65.55.36C41.74,16.56,43.65,11,37.93,5,37.64,4.74,37.19,5.14,37.39,5.49Zm2.76-.17A16.42,16.42,0,0,1,47,17.12a.33.33,0,0,0,.65.11c.92-3.49.4-9.44-7.17-12.53C40.08,4.54,39.82,5.08,40.15,5.32ZM21.69,15.76a16.94,16.94,0,0,0,10.47-9c.18-.36.75-.22.66.18-1.73,8-7.52,9.67-11.12,9.45C21.32,16.4,21.33,15.87,21.69,15.76Z"
29+
style={{ fill: "#ccbea7", fillRule: "evenodd" }}
30+
/>
31+
<path
32+
id="Outline"
33+
d="M38,65.75C17.32,65.75.5,52.27.5,35.7c0-10,6.18-19.33,16.53-24.92,3-1.6,5.57-3.21,7.86-4.62,1.26-.78,2.45-1.51,3.6-2.19C32,1.89,35,.5,38,.5s5.62,1.2,8.9,3.14c1,.57,2,1.19,3.07,1.87,2.49,1.54,5.3,3.28,9,5.27C69.32,16.37,75.5,25.69,75.5,35.7,75.5,52.27,58.68,65.75,38,65.75ZM38,3c-2.42,0-5,1.25-8.25,3.13-1.13.66-2.3,1.39-3.54,2.15-2.33,1.44-5,3.07-8,4.7C8.69,18.13,3,26.62,3,35.7,3,50.89,18.7,63.25,38,63.25S73,50.89,73,35.7C73,26.62,67.31,18.13,57.78,13,54,11,51.05,9.12,48.66,7.64c-1.09-.67-2.09-1.29-3-1.84C42.63,4,40.42,3,38,3Z"
34+
/>
35+
</g>
36+
<g id="Mouth">
37+
<g id="Background-2" data-name="Background">
38+
<path
39+
d="M45.05,43a8.93,8.93,0,0,1-2.92,4.71,6.81,6.81,0,0,1-4,1.88A6.84,6.84,0,0,1,34,47.71,8.93,8.93,0,0,1,31.12,43a.72.72,0,0,1,.8-.81H44.26A.72.72,0,0,1,45.05,43Z"
40+
style={{ fill: "#b71422" }}
41+
/>
42+
</g>
43+
<g id="Tongue">
44+
<path
45+
id="Background-3"
46+
data-name="Background"
47+
d="M34,47.79a6.91,6.91,0,0,0,4.12,1.9,6.91,6.91,0,0,0,4.11-1.9,10.63,10.63,0,0,0,1-1.07,6.83,6.83,0,0,0-4.9-2.31,6.15,6.15,0,0,0-5,2.78C33.56,47.4,33.76,47.6,34,47.79Z"
48+
style={{ fill: "#ff6164" }}
49+
/>
50+
<path
51+
id="Outline-2"
52+
data-name="Outline"
53+
d="M34.16,47a5.36,5.36,0,0,1,4.19-2.08,6,6,0,0,1,4,1.69c.23-.25.45-.51.66-.77a7,7,0,0,0-4.71-1.93,6.36,6.36,0,0,0-4.89,2.36A9.53,9.53,0,0,0,34.16,47Z"
54+
/>
55+
</g>
56+
<path
57+
id="Outline-3"
58+
data-name="Outline"
59+
d="M38.09,50.19a7.42,7.42,0,0,1-4.45-2,9.52,9.52,0,0,1-3.11-5.05,1.2,1.2,0,0,1,.26-1,1.41,1.41,0,0,1,1.13-.51H44.26a1.44,1.44,0,0,1,1.13.51,1.19,1.19,0,0,1,.25,1h0a9.52,9.52,0,0,1-3.11,5.05A7.42,7.42,0,0,1,38.09,50.19Zm-6.17-7.4c-.16,0-.2.07-.21.09a8.29,8.29,0,0,0,2.73,4.37A6.23,6.23,0,0,0,38.09,49a6.28,6.28,0,0,0,3.65-1.73,8.3,8.3,0,0,0,2.72-4.37.21.21,0,0,0-.2-.09Z"
60+
/>
61+
</g>
62+
<g id="Face">
63+
<ellipse
64+
id="Right_Blush"
65+
data-name="Right Blush"
66+
cx="53.22"
67+
cy="40.18"
68+
rx="5.85"
69+
ry="3.44"
70+
style={{ fill: "#febbd0" }}
71+
/>
72+
<ellipse
73+
id="Left_Bluch"
74+
data-name="Left Bluch"
75+
cx="22.95"
76+
cy="40.18"
77+
rx="5.85"
78+
ry="3.44"
79+
style={{ fill: "#febbd0" }}
80+
/>
81+
<path
82+
id="Eyes"
83+
d="M25.7,38.8a5.51,5.51,0,1,0-5.5-5.51A5.51,5.51,0,0,0,25.7,38.8Zm24.77,0A5.51,5.51,0,1,0,45,33.29,5.5,5.5,0,0,0,50.47,38.8Z"
84+
style={{ fillRule: "evenodd" }}
85+
/>
86+
<path
87+
id="Iris"
88+
d="M24,33.64a2.07,2.07,0,1,0-2.06-2.07A2.07,2.07,0,0,0,24,33.64Zm24.77,0a2.07,2.07,0,1,0-2.06-2.07A2.07,2.07,0,0,0,48.75,33.64Z"
89+
style={{ fill: "#fff", fillRule: "evenodd" }}
90+
/>
91+
</g>
92+
</svg>
93+
);
94+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export function NodejsLogoIcon({ className }: { className?: string }) {
2+
return (
3+
<svg className={className} viewBox="0 0 71 80" fill="none" xmlns="http://www.w3.org/2000/svg">
4+
<g clipPath="url(#clip0_337_7891)" fill="#5FA04E">
5+
<path d="M35.625 79.5c-1.081 0-2.09-.288-3.028-.792l-9.59-5.686c-1.442-.792-.721-1.08-.289-1.224 1.947-.648 2.308-.792 4.327-1.944.216-.144.504-.072.72.072l7.356 4.391c.288.144.649.144.865 0l28.77-16.628c.289-.144.433-.431.433-.791V23.714c0-.36-.144-.648-.432-.792L35.986 6.366c-.288-.144-.65-.144-.865 0L6.35 22.922c-.29.144-.434.504-.434.792v33.184c0 .287.145.647.433.791l7.86 4.535c4.254 2.16 6.922-.36 6.922-2.879V26.593c0-.432.36-.864.865-.864h3.678c.432 0 .865.36.865.864v32.752c0 5.687-3.1 8.998-8.509 8.998-1.658 0-2.956 0-6.633-1.8l-7.572-4.319A6.073 6.073 0 0 1 .798 56.97V23.786a6.073 6.073 0 0 1 3.028-5.255l28.77-16.628c1.804-1.008 4.255-1.008 6.058 0l28.77 16.628a6.073 6.073 0 0 1 3.029 5.255V56.97a6.073 6.073 0 0 1-3.029 5.254l-28.77 16.628c-.865.36-1.947.648-3.029.648Z" />
6+
<path d="M44.567 56.682c-12.62 0-15.215-5.759-15.215-10.654 0-.432.36-.864.865-.864h3.75c.433 0 .793.288.793.72.577 3.815 2.235 5.687 9.879 5.687 6.057 0 8.652-1.368 8.652-4.607 0-1.871-.72-3.24-10.167-4.175-7.86-.792-12.762-2.52-12.762-8.782 0-5.83 4.903-9.285 13.123-9.285 9.23 0 13.772 3.167 14.35 10.077 0 .216-.073.432-.217.648-.144.144-.36.288-.577.288h-3.822a.844.844 0 0 1-.793-.648c-.865-3.96-3.1-5.255-9.013-5.255-6.634 0-7.427 2.304-7.427 4.031 0 2.088.937 2.736 9.879 3.887 8.869 1.152 13.05 2.808 13.05 8.998 0 6.335-5.263 9.934-14.348 9.934Z" />
7+
</g>
8+
<defs>
9+
<clipPath id="clip0_337_7891">
10+
<path fill="#fff" d="M0 .5h71v79H0z" />
11+
</clipPath>
12+
</defs>
13+
</svg>
14+
);
15+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { SimpleTooltip } from "~/components/primitives/Tooltip";
2+
import { BunLogoIcon } from "~/assets/icons/BunLogoIcon";
3+
import { NodejsLogoIcon } from "~/assets/icons/NodejsLogoIcon";
4+
import { parseRuntime, formatRuntimeWithVersion } from "~/utils/runtime";
5+
6+
interface RuntimeIconProps {
7+
runtime?: string | null;
8+
runtimeVersion?: string | null;
9+
className?: string;
10+
withLabel?: boolean;
11+
}
12+
13+
export function RuntimeIcon({
14+
runtime,
15+
runtimeVersion,
16+
className = "h-4 w-4",
17+
withLabel = false,
18+
}: RuntimeIconProps) {
19+
const parsedRuntime = parseRuntime(runtime);
20+
21+
if (!parsedRuntime) {
22+
return <span className="text-text-dimmed"></span>;
23+
}
24+
25+
const getIcon = () => {
26+
switch (parsedRuntime.runtime) {
27+
case "bun":
28+
return <BunLogoIcon className={className} />;
29+
case "node":
30+
return <NodejsLogoIcon className={className} />;
31+
default:
32+
return <span className="text-text-dimmed"></span>;
33+
}
34+
};
35+
36+
const icon = getIcon();
37+
const formattedText = formatRuntimeWithVersion(runtime, runtimeVersion);
38+
39+
if (withLabel) {
40+
return (
41+
<span className="flex items-center gap-1">
42+
{icon}
43+
<span>{formattedText}</span>
44+
</span>
45+
);
46+
}
47+
48+
if (typeof icon === "object" && "type" in icon) {
49+
return (
50+
<SimpleTooltip button={icon} content={formattedText} side="top" disableHoverableContent />
51+
);
52+
}
53+
54+
return icon;
55+
}

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
33
import { typedjson, useTypedLoaderData } from "remix-typedjson";
44
import { ExitIcon } from "~/assets/icons/ExitIcon";
55
import { GitMetadata } from "~/components/GitMetadata";
6+
import { RuntimeIcon } from "~/components/RuntimeIcon";
67
import { UserAvatar } from "~/components/UserProfilePhoto";
78
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
89
import { EnvironmentCombo } from "~/components/environments/EnvironmentLabel";
@@ -172,16 +173,11 @@ export default function Page() {
172173
<Property.Item>
173174
<Property.Label>Runtime</Property.Label>
174175
<Property.Value>
175-
{deployment.runtime ? (
176-
<>
177-
{deployment.runtime}
178-
{deployment.runtimeVersion && (
179-
<span className="ml-1 text-text-dimmed">v{deployment.runtimeVersion}</span>
180-
)}
181-
</>
182-
) : (
183-
"–"
184-
)}
176+
<RuntimeIcon
177+
runtime={deployment.runtime}
178+
runtimeVersion={deployment.runtimeVersion}
179+
withLabel
180+
/>
185181
</Property.Value>
186182
</Property.Item>
187183
<Property.Item>

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { z } from "zod";
77
import { PromoteIcon } from "~/assets/icons/PromoteIcon";
88
import { DeploymentsNone, DeploymentsNoneDev } from "~/components/BlankStatePanels";
99
import { GitMetadata } from "~/components/GitMetadata";
10+
import { RuntimeIcon } from "~/components/RuntimeIcon";
1011
import { UserAvatar } from "~/components/UserProfilePhoto";
1112
import { MainCenteredContainer, PageBody, PageContainer } from "~/components/layout/AppLayout";
1213
import { Badge } from "~/components/primitives/Badge";
@@ -166,7 +167,6 @@ export default function Page() {
166167
<TableRow>
167168
<TableHeaderCell>Deploy</TableHeaderCell>
168169
<TableHeaderCell>Version</TableHeaderCell>
169-
<TableHeaderCell>Runtime</TableHeaderCell>
170170
<TableHeaderCell
171171
tooltip={
172172
<div className="flex flex-col divide-y divide-grid-dimmed">
@@ -191,6 +191,7 @@ export default function Page() {
191191
>
192192
Status
193193
</TableHeaderCell>
194+
<TableHeaderCell>Runtime</TableHeaderCell>
194195
<TableHeaderCell>Tasks</TableHeaderCell>
195196
<TableHeaderCell>Deployed at</TableHeaderCell>
196197
<TableHeaderCell>Deployed by</TableHeaderCell>
@@ -222,26 +223,18 @@ export default function Page() {
222223
<TableCell to={path} isSelected={isSelected}>
223224
{deployment.version}
224225
</TableCell>
225-
<TableCell to={path} isSelected={isSelected}>
226-
{deployment.runtime ? (
227-
<>
228-
{deployment.runtime}
229-
{deployment.runtimeVersion && (
230-
<span className="ml-1 text-text-dimmed">
231-
v{deployment.runtimeVersion}
232-
</span>
233-
)}
234-
</>
235-
) : (
236-
"–"
237-
)}
238-
</TableCell>
239226
<TableCell to={path} isSelected={isSelected}>
240227
<DeploymentStatus
241228
status={deployment.status}
242229
isBuilt={deployment.isBuilt}
243230
/>
244231
</TableCell>
232+
<TableCell to={path} isSelected={isSelected}>
233+
<RuntimeIcon
234+
runtime={deployment.runtime}
235+
runtimeVersion={deployment.runtimeVersion}
236+
/>
237+
</TableCell>
245238
<TableCell to={path} isSelected={isSelected}>
246239
{deployment.tasksCount !== null ? deployment.tasksCount : "–"}
247240
</TableCell>

apps/webapp/app/utils/runtime.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
export type NormalizedRuntime = "node" | "bun";
2+
3+
export interface ParsedRuntime {
4+
/** The normalized runtime type */
5+
runtime: NormalizedRuntime;
6+
/** The original runtime string */
7+
originalRuntime: string;
8+
/** The display name for the runtime */
9+
displayName: string;
10+
}
11+
12+
/**
13+
* Parses a runtime string and returns normalized runtime information
14+
*/
15+
export function parseRuntime(runtime: string | null | undefined): ParsedRuntime | null {
16+
if (!runtime) {
17+
return null;
18+
}
19+
20+
// Normalize runtime strings
21+
let normalizedRuntime: NormalizedRuntime;
22+
let displayName: string;
23+
24+
if (runtime.startsWith("bun")) {
25+
normalizedRuntime = "bun";
26+
displayName = "Bun";
27+
} else if (runtime.startsWith("node")) {
28+
normalizedRuntime = "node";
29+
displayName = "Node.js";
30+
} else {
31+
return null;
32+
}
33+
34+
return {
35+
runtime: normalizedRuntime,
36+
originalRuntime: runtime,
37+
displayName,
38+
};
39+
}
40+
41+
/**
42+
* Formats runtime with version for display
43+
*/
44+
export function formatRuntimeWithVersion(
45+
runtime: string | null | undefined,
46+
version: string | null | undefined
47+
): string {
48+
const parsed = parseRuntime(runtime);
49+
if (!parsed) {
50+
return "Unknown runtime";
51+
}
52+
53+
if (version) {
54+
return `${parsed.displayName} v${version}`;
55+
}
56+
57+
return parsed.displayName;
58+
}

0 commit comments

Comments
 (0)