feat(site): improve icon compatibility across themes (#11457)

This commit is contained in:
Kayla Washburn 2024-01-08 14:12:40 -07:00 committed by GitHub
parent 427afe13e0
commit 9f5a59d5c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 722 additions and 196 deletions

View File

@ -5,7 +5,7 @@
"url": "",
"name": "AWS EC2 (Devcontainer)",
"description": "Provision AWS EC2 VMs with a devcontainer as Coder workspaces",
"icon": "/icon/aws.png",
"icon": "/icon/aws.svg",
"tags": [
"vm",
"linux",
@ -20,7 +20,7 @@
"url": "",
"name": "AWS EC2 (Linux)",
"description": "Provision AWS EC2 VMs as Coder workspaces",
"icon": "/icon/aws.png",
"icon": "/icon/aws.svg",
"tags": [
"vm",
"linux",
@ -34,7 +34,7 @@
"url": "",
"name": "AWS EC2 (Windows)",
"description": "Provision AWS EC2 VMs as Coder workspaces",
"icon": "/icon/aws.png",
"icon": "/icon/aws.svg",
"tags": [
"vm",
"windows",

View File

@ -1,7 +1,7 @@
---
display_name: AWS EC2 (Devcontainer)
description: Provision AWS EC2 VMs with a devcontainer as Coder workspaces
icon: ../../../site/static/icon/aws.png
icon: ../../../site/static/icon/aws.svg
maintainer_github: coder
verified: true
tags: [vm, linux, aws, persistent, devcontainer]

View File

@ -1,7 +1,7 @@
---
display_name: AWS EC2 (Linux)
description: Provision AWS EC2 VMs as Coder workspaces
icon: ../../../site/static/icon/aws.png
icon: ../../../site/static/icon/aws.svg
maintainer_github: coder
verified: true
tags: [vm, linux, aws, persistent-vm]

View File

@ -1,7 +1,7 @@
---
display_name: AWS EC2 (Windows)
description: Provision AWS EC2 VMs as Coder workspaces
icon: ../../../site/static/icon/aws.png
icon: ../../../site/static/icon/aws.svg
maintainer_github: coder
verified: true
tags: [vm, windows, aws]

View File

@ -21,8 +21,6 @@ import WorkspacesPage from "./pages/WorkspacesPage/WorkspacesPage";
import UserSettingsLayout from "./pages/UserSettingsPage/Layout";
import { TemplateSettingsLayout } from "./pages/TemplateSettingsPage/TemplateSettingsLayout";
import { WorkspaceSettingsLayout } from "./pages/WorkspaceSettingsPage/WorkspaceSettingsLayout";
import { ThemeOverride } from "contexts/ThemeProvider";
import themes from "theme";
// Lazy load pages
// - Pages that are secondary, not in the main navigation or not usually accessed
@ -421,11 +419,7 @@ export const AppRouter: FC = () => {
/>
<Route
path="/:username/:workspace/terminal"
element={
<ThemeOverride theme={themes.dark}>
<TerminalPage />
</ThemeOverride>
}
element={<TerminalPage />}
/>
<Route path="/cli-auth" element={<CliAuthenticationPage />} />
<Route path="/icons" element={<IconsPage />} />

View File

@ -1,6 +1,6 @@
import { FC, PropsWithChildren } from "react";
const ReactMarkdown: FC<PropsWithChildren<unknown>> = ({ children }) => {
const ReactMarkdown: FC<PropsWithChildren> = ({ children }) => {
return <div data-testid="markdown">{children}</div>;
};

View File

@ -131,6 +131,7 @@ export const me = (): UseQueryOptions<User> & {
queryKey: meKey,
initialData: initialUserData,
queryFn: API.getAuthenticatedUser,
refetchOnWindowFocus: true,
};
};

View File

@ -4,8 +4,9 @@ import MuiAvatar, {
type AvatarProps as MuiAvatarProps,
} from "@mui/material/Avatar";
import { type FC, useId } from "react";
import { css, type Interpolation, type Theme } from "@emotion/react";
import { css, type Interpolation, type Theme, useTheme } from "@emotion/react";
import { visuallyHidden } from "@mui/utils";
import { getExternalImageStylesFromUrl } from "theme/externalImages";
export type AvatarProps = MuiAvatarProps & {
size?: "xs" | "sm" | "md" | "xl";
@ -67,6 +68,17 @@ export const Avatar: FC<AvatarProps> = ({
);
};
export const ExternalAvatar: FC<AvatarProps> = (props) => {
const theme = useTheme();
return (
<Avatar
css={getExternalImageStylesFromUrl(theme.externalImages, props.src)}
{...props}
/>
);
};
type AvatarIconProps = {
src: string;
alt: string;

View File

@ -1,4 +1,4 @@
import { type ReactNode } from "react";
import { type FC, type ReactNode } from "react";
import { Avatar } from "components/Avatar/Avatar";
import { type CSSObject, useTheme } from "@emotion/react";
@ -12,14 +12,14 @@ type AvatarCardProps = {
maxWidth?: number | "none";
};
export function AvatarCard({
export const AvatarCard: FC<AvatarCardProps> = ({
header,
imgUrl,
altText,
background,
subtitle,
maxWidth = "none",
}: AvatarCardProps) {
}) => {
const theme = useTheme();
return (
@ -77,4 +77,4 @@ export function AvatarCard({
</Avatar>
</div>
);
}
};

View File

@ -1,36 +0,0 @@
import { type Interpolation, type Theme } from "@emotion/react";
import { type FC, type ImgHTMLAttributes } from "react";
interface ExternalIconProps extends ImgHTMLAttributes<HTMLImageElement> {
size?: number;
}
export const ExternalIcon: FC<ExternalIconProps> = ({
size = 36,
...attrs
}) => {
return (
<div css={[styles.container, { height: size, width: size }]}>
<img
alt=""
aria-hidden
css={[
styles.icon,
{ height: size, width: size, padding: Math.ceil(size / 6) },
]}
{...attrs}
/>
</div>
);
};
const styles = {
container: {
borderRadius: 9999,
overflow: "clip",
},
icon: {
backgroundColor: "#000",
objectFit: "contain",
},
} satisfies Record<string, Interpolation<Theme>>;

View File

@ -0,0 +1,19 @@
import { useTheme } from "@emotion/react";
import { type ImgHTMLAttributes, forwardRef } from "react";
import { getExternalImageStylesFromUrl } from "theme/externalImages";
export const ExternalImage = forwardRef<
HTMLImageElement,
ImgHTMLAttributes<HTMLImageElement>
>((attrs, ref) => {
const theme = useTheme();
return (
<img
ref={ref}
alt=""
css={getExternalImageStylesFromUrl(theme.externalImages, attrs.src)}
{...attrs}
/>
);
});

View File

@ -2,17 +2,17 @@ import { css } from "@emotion/css";
import Button, { ButtonProps } from "@mui/material/Button";
import IconButton, { IconButtonProps } from "@mui/material/IconButton";
import { useTheme } from "@mui/material/styles";
import { Avatar, AvatarProps } from "components/Avatar/Avatar";
import { AvatarProps, ExternalAvatar } from "components/Avatar/Avatar";
import {
ForwardedRef,
HTMLAttributes,
PropsWithChildren,
ReactElement,
type FC,
type ForwardedRef,
type HTMLAttributes,
type ReactElement,
cloneElement,
forwardRef,
} from "react";
export const Topbar = (props: HTMLAttributes<HTMLDivElement>) => {
export const Topbar: FC<HTMLAttributes<HTMLElement>> = (props) => {
const theme = useTheme();
return (
@ -70,7 +70,7 @@ export const TopbarButton = forwardRef<HTMLButtonElement, ButtonProps>(
},
);
export const TopbarData = (props: HTMLAttributes<HTMLDivElement>) => {
export const TopbarData: FC<HTMLAttributes<HTMLDivElement>> = (props) => {
return (
<div
{...props}
@ -84,7 +84,7 @@ export const TopbarData = (props: HTMLAttributes<HTMLDivElement>) => {
);
};
export const TopbarDivider = (props: HTMLAttributes<HTMLSpanElement>) => {
export const TopbarDivider: FC<HTMLAttributes<HTMLSpanElement>> = (props) => {
const theme = useTheme();
return (
<span {...props} css={{ color: theme.palette.divider }}>
@ -93,9 +93,9 @@ export const TopbarDivider = (props: HTMLAttributes<HTMLSpanElement>) => {
);
};
export const TopbarAvatar = (props: AvatarProps) => {
export const TopbarAvatar: FC<AvatarProps> = (props) => {
return (
<Avatar
<ExternalAvatar
{...props}
variant="square"
fitImage
@ -104,7 +104,7 @@ export const TopbarAvatar = (props: AvatarProps) => {
);
};
type TopbarIconProps = PropsWithChildren<HTMLAttributes<HTMLOrSVGElement>>;
type TopbarIconProps = HTMLAttributes<HTMLOrSVGElement>;
export const TopbarIcon = forwardRef<HTMLOrSVGElement, TopbarIconProps>(
(props: TopbarIconProps, ref) => {

View File

@ -19,7 +19,7 @@ export const GroupAvatar: FC<GroupAvatarProps> = ({ name, avatarURL }) => {
badgeContent={<Group />}
classes={{ badge }}
>
<Avatar src={avatarURL} background>
<Avatar background src={avatarURL}>
{name}
</Avatar>
</Badge>

View File

@ -12,6 +12,7 @@ import {
PopoverContent,
PopoverTrigger,
} from "components/Popover/Popover";
import { ExternalImage } from "components/ExternalImage/ExternalImage";
// See: https://github.com/missive/emoji-mart/issues/51#issuecomment-287353222
const urlFromUnifiedCode = (unified: string) =>
@ -60,7 +61,7 @@ export const IconField: FC<IconFieldProps> = ({
},
}}
>
<img
<ExternalImage
alt=""
src={textFieldProps.value}
// This prevent browser to display the ugly error icon if the

View File

@ -1,16 +1,26 @@
import { type FC } from "react";
import { type FC, useId } from "react";
import { visuallyHidden } from "@mui/utils";
import type { WorkspaceResource } from "api/typesGenerated";
import { Avatar, AvatarIcon } from "components/Avatar/Avatar";
import { Avatar } from "components/Avatar/Avatar";
import { ExternalImage } from "components/ExternalImage/ExternalImage";
import { getResourceIconPath } from "utils/workspace";
export type ResourceAvatarProps = { resource: WorkspaceResource };
export const ResourceAvatar: FC<ResourceAvatarProps> = ({ resource }) => {
const avatarSrc = resource.icon || getResourceIconPath(resource.type);
const altId = useId();
return (
<Avatar background>
<AvatarIcon src={avatarSrc} alt={resource.name} />
<ExternalImage
src={avatarSrc}
css={{ maxWidth: "50%" }}
aria-labelledby={altId}
/>
<div id={altId} css={{ ...visuallyHidden }}>
{resource.name}
</div>
</Avatar>
);
};

View File

@ -96,7 +96,7 @@ export const Options: Story = {
name: "Third option",
value: "third_option",
description: "",
icon: "/icon/aws.png",
icon: "/icon/aws.svg",
},
],
}),
@ -138,7 +138,7 @@ Very big.
> Wow, that description is straight up large. Some guy, probably
`,
icon: "/icon/aws.png",
icon: "/icon/aws.svg",
},
],
}),

View File

@ -9,6 +9,7 @@ import { TemplateVersionParameter } from "api/typesGenerated";
import { MemoizedMarkdown } from "components/Markdown/Markdown";
import { Stack } from "components/Stack/Stack";
import { MultiTextField } from "./MultiTextField";
import { ExternalImage } from "components/ExternalImage/ExternalImage";
const isBoolean = (parameter: TemplateVersionParameter) => {
return parameter.type === "bool";
@ -106,7 +107,7 @@ const ParameterLabel: FC<ParameterLabelProps> = ({ parameter }) => {
<Stack direction="row" alignItems="center">
{parameter.icon && (
<span css={styles.labelIconWrapper}>
<img
<ExternalImage
css={styles.labelIcon}
alt="Parameter icon"
src={parameter.icon}
@ -213,7 +214,7 @@ const RichParameterField: FC<RichParameterInputProps> = ({
label={
<Stack direction="row" alignItems="center">
{option.icon && (
<img
<ExternalImage
css={styles.optionIcon}
src={option.icon}
alt="Parameter icon"

View File

@ -0,0 +1,36 @@
import type { Meta, StoryObj } from "@storybook/react";
import { chromatic } from "testHelpers/chromatic";
import {
MockTemplateExample,
MockTemplateExample2,
} from "testHelpers/entities";
import { TemplateExampleCard } from "./TemplateExampleCard";
const meta: Meta<typeof TemplateExampleCard> = {
title: "components/TemplateExampleCard",
parameters: { chromatic },
component: TemplateExampleCard,
args: {
example: MockTemplateExample,
},
};
export default meta;
type Story = StoryObj<typeof TemplateExampleCard>;
export const Example: Story = {};
export const ByTag: Story = {
args: {
activeTag: "cloud",
},
};
export const LotsOfTags: Story = {
args: {
example: {
...MockTemplateExample2,
tags: ["omg", "so many tags", "look at all these", "so cool"],
},
},
};

View File

@ -1,80 +1,40 @@
import { type Interpolation, type Theme } from "@emotion/react";
import Button from "@mui/material/Button";
import Link from "@mui/material/Link";
import type { TemplateExample } from "api/typesGenerated";
import { ExternalImage } from "components/ExternalImage/ExternalImage";
import { Pill } from "components/Pill/Pill";
import { HTMLProps } from "react";
import { type FC, type HTMLAttributes } from "react";
import { Link as RouterLink } from "react-router-dom";
type TemplateExampleCardProps = {
type TemplateExampleCardProps = HTMLAttributes<HTMLDivElement> & {
example: TemplateExample;
activeTag?: string;
} & HTMLProps<HTMLDivElement>;
};
export const TemplateExampleCard = (props: TemplateExampleCardProps) => {
const { example, activeTag, ...divProps } = props;
export const TemplateExampleCard: FC<TemplateExampleCardProps> = ({
example,
activeTag,
...divProps
}) => {
return (
<div
css={(theme) => ({
width: "320px",
padding: 24,
borderRadius: 6,
border: `1px solid ${theme.palette.divider}`,
textAlign: "left",
textDecoration: "none",
color: "inherit",
display: "flex",
flexDirection: "column",
})}
{...divProps}
>
<div
css={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
marginBottom: 24,
}}
>
<div
css={{
flexShrink: 0,
paddingTop: 4,
width: 32,
height: 32,
}}
>
<img
<div css={styles.card} {...divProps}>
<div css={styles.header}>
<div css={styles.icon}>
<ExternalImage
src={example.icon}
alt=""
css={{ width: "100%", height: "100%", objectFit: "contain" }}
/>
</div>
<div css={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
{example.tags.map((tag) => {
const isActive = activeTag === tag;
return (
<RouterLink key={tag} to={`/starter-templates?tag=${tag}`}>
<Pill
css={(theme) => ({
borderColor: isActive
? theme.palette.primary.main
: theme.palette.divider,
cursor: "pointer",
backgroundColor: isActive
? theme.palette.primary.dark
: undefined,
"&: hover": {
borderColor: theme.palette.primary.main,
},
})}
>
{tag}
</Pill>
</RouterLink>
);
})}
<div css={styles.tags}>
{example.tags.map((tag) => (
<RouterLink key={tag} to={`/starter-templates?tag=${tag}`}>
<Pill css={[styles.tag, activeTag === tag && styles.activeTag]}>
{tag}
</Pill>
</RouterLink>
))}
</div>
</div>
@ -82,14 +42,7 @@ export const TemplateExampleCard = (props: TemplateExampleCardProps) => {
<h4 css={{ fontSize: 14, fontWeight: 600, margin: 0, marginBottom: 4 }}>
{example.name}
</h4>
<span
css={(theme) => ({
fontSize: 13,
color: theme.palette.text.secondary,
lineHeight: "1.6",
display: "block",
})}
>
<span css={styles.description}>
{example.description}{" "}
<Link
component={RouterLink}
@ -101,16 +54,7 @@ export const TemplateExampleCard = (props: TemplateExampleCardProps) => {
</span>
</div>
<div
css={{
display: "flex",
gap: 12,
flexDirection: "column",
paddingTop: 24,
marginTop: "auto",
alignItems: "center",
}}
>
<div css={styles.useButtonContainer}>
<Button
component={RouterLink}
fullWidth
@ -122,3 +66,67 @@ export const TemplateExampleCard = (props: TemplateExampleCardProps) => {
</div>
);
};
const styles = {
card: (theme) => ({
width: "320px",
padding: 24,
borderRadius: 6,
border: `1px solid ${theme.palette.divider}`,
textAlign: "left",
color: "inherit",
display: "flex",
flexDirection: "column",
}),
header: {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
marginBottom: 24,
},
icon: {
flexShrink: 0,
paddingTop: 4,
width: 32,
height: 32,
},
tags: {
display: "flex",
flexWrap: "wrap",
gap: 8,
justifyContent: "end",
},
tag: (theme) => ({
borderColor: theme.palette.divider,
textDecoration: "none",
cursor: "pointer",
"&: hover": {
borderColor: theme.palette.primary.main,
},
}),
activeTag: (theme) => ({
borderColor: theme.experimental.roles.active.outline,
backgroundColor: theme.experimental.roles.active.background,
}),
description: (theme) => ({
fontSize: 13,
color: theme.palette.text.secondary,
lineHeight: "1.6",
display: "block",
}),
useButtonContainer: {
display: "flex",
gap: 12,
flexDirection: "column",
paddingTop: 24,
marginTop: "auto",
alignItems: "center",
},
} satisfies Record<string, Interpolation<Theme>>;

View File

@ -1,8 +1,8 @@
import { FullPageHorizontalForm } from "components/FullPageForm/FullPageHorizontalForm";
import { FC } from "react";
import { type FC } from "react";
import { Helmet } from "react-helmet-async";
import { useNavigate, useSearchParams } from "react-router-dom";
import { pageTitle } from "utils/page";
import { FullPageHorizontalForm } from "components/FullPageForm/FullPageHorizontalForm";
import { DuplicateTemplateView } from "./DuplicateTemplateView";
import { ImportStarterTemplateView } from "./ImportStarterTemplateView";
import { UploadTemplateView } from "./UploadTemplateView";

View File

@ -1,6 +1,6 @@
import { type FC } from "react";
import type { Template, TemplateExample } from "api/typesGenerated";
import { Avatar } from "components/Avatar/Avatar";
import { ExternalAvatar } from "components/Avatar/Avatar";
import { Stack } from "components/Stack/Stack";
import { type Interpolation, type Theme } from "@emotion/react";
@ -16,13 +16,13 @@ export const SelectedTemplate: FC<SelectedTemplateProps> = ({ template }) => {
css={styles.template}
alignItems="center"
>
<Avatar
<ExternalAvatar
variant={template.icon ? "square" : undefined}
fitImage={Boolean(template.icon)}
src={template.icon}
>
{template.name}
</Avatar>
</ExternalAvatar>
<Stack direction="column" spacing={0}>
<span css={styles.templateName}>

View File

@ -0,0 +1,17 @@
import type { Meta, StoryObj } from "@storybook/react";
import { chromatic } from "testHelpers/chromatic";
import { IconsPage } from "./IconsPage";
const meta: Meta<typeof IconsPage> = {
title: "pages/IconsPage",
parameters: { chromatic },
component: IconsPage,
args: {},
};
export default meta;
type Story = StoryObj<typeof IconsPage>;
const Example: Story = {};
export { Example as IconsPage };

View File

@ -19,6 +19,10 @@ import {
} from "components/PageHeader/PageHeader";
import { Stack } from "components/Stack/Stack";
import icons from "theme/icons.json";
import {
defaultParametersForBuiltinIcons,
parseImageParameters,
} from "theme/externalImages";
import { pageTitle } from "utils/page";
const iconsWithoutSuffix = icons.map((icon) => icon.split(".")[0]);
@ -163,13 +167,19 @@ export const IconsPage: FC = () => {
<img
alt={icon.url}
src={icon.url}
css={{
width: 60,
height: 60,
objectFit: "contain",
pointerEvents: "none",
padding: 12,
}}
css={[
{
width: 60,
height: 60,
objectFit: "contain",
pointerEvents: "none",
padding: 12,
},
parseImageParameters(
theme.externalImages,
defaultParametersForBuiltinIcons.get(icon.url) ?? "",
),
]}
/>
<figcaption
css={{

View File

@ -1,3 +1,4 @@
import { chromatic } from "testHelpers/chromatic";
import { mockApiError, MockTemplateExample } from "testHelpers/entities";
import { StarterTemplatePageView } from "./StarterTemplatePageView";
@ -5,6 +6,7 @@ import type { Meta, StoryObj } from "@storybook/react";
const meta: Meta<typeof StarterTemplatePageView> = {
title: "pages/StarterTemplatePage",
parameters: { chromatic },
component: StarterTemplatePageView,
};

View File

@ -15,6 +15,7 @@ import { Stack } from "components/Stack/Stack";
import { Link } from "react-router-dom";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import type { TemplateExample } from "api/typesGenerated";
import { ExternalImage } from "components/ExternalImage/ExternalImage";
export interface StarterTemplatePageViewProps {
starterTemplate?: TemplateExample;
@ -78,7 +79,7 @@ export const StarterTemplatePageView: FC<StarterTemplatePageViewProps> = ({
},
}}
>
<img src={starterTemplate.icon} alt="" />
<ExternalImage src={starterTemplate.icon} />
</div>
<div>
<PageHeaderTitle>{starterTemplate.name}</PageHeaderTitle>

View File

@ -1,3 +1,4 @@
import { chromatic } from "testHelpers/chromatic";
import {
mockApiError,
MockTemplateExample,
@ -9,6 +10,7 @@ import type { Meta, StoryObj } from "@storybook/react";
const meta: Meta<typeof StarterTemplatesPageView> = {
title: "pages/StarterTemplatesPage",
parameters: { chromatic },
component: StarterTemplatesPageView,
};

View File

@ -10,6 +10,7 @@ import {
SidebarHeader,
SidebarNavItem,
} from "components/Sidebar/Sidebar";
import { ExternalImage } from "components/ExternalImage/ExternalImage";
interface SidebarProps {
template: Template;
@ -19,7 +20,11 @@ export const Sidebar: FC<SidebarProps> = ({ template }) => {
return (
<BaseSidebar>
<SidebarHeader
avatar={<Avatar src={template.icon} variant="square" fitImage />}
avatar={
<Avatar variant="square" fitImage>
<ExternalImage src={template.icon} css={{ width: "100%" }} />
</Avatar>
}
title={template.display_name || template.name}
linkTo={`/templates/${template.name}`}
subtitle={template.name}

View File

@ -39,7 +39,7 @@ import { EmptyTemplates } from "./EmptyTemplates";
import { useClickableTableRow } from "hooks/useClickableTableRow";
import type { Template, TemplateExample } from "api/typesGenerated";
import ArrowForwardOutlined from "@mui/icons-material/ArrowForwardOutlined";
import { Avatar } from "components/Avatar/Avatar";
import { ExternalAvatar } from "components/Avatar/Avatar";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { docs } from "utils/docs";
import Skeleton from "@mui/material/Skeleton";
@ -108,7 +108,9 @@ const TemplateRow: FC<TemplateRowProps> = ({ template }) => {
}
subtitle={template.description}
avatar={
hasIcon && <Avatar src={template.icon} variant="square" fitImage />
hasIcon && (
<ExternalAvatar variant="square" fitImage src={template.icon} />
)
}
/>
</TableCell>

View File

@ -33,6 +33,8 @@ import {
PopoverContent,
PopoverTrigger,
} from "components/Popover/Popover";
import { ThemeOverride } from "contexts/ThemeProvider";
import themes from "theme";
export const Language = {
workspaceErrorMessagePrefix: "Unable to fetch workspace: ",
@ -293,7 +295,7 @@ const TerminalPage: FC = () => {
]);
return (
<>
<ThemeOverride theme={themes.dark}>
<Helmet>
<title>
{workspace.data
@ -314,7 +316,7 @@ const TerminalPage: FC = () => {
<BottomBar proxy={selectedProxy} latency={latency.latencyMS} />
)}
</div>
</>
</ThemeOverride>
);
};

View File

@ -66,7 +66,7 @@ export interface WorkspaceProps {
/**
* Workspace is the top-level component for viewing an individual workspace
*/
export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
export const Workspace: FC<WorkspaceProps> = ({
handleStart,
handleStop,
handleRestart,

View File

@ -29,7 +29,7 @@ import PersonOutline from "@mui/icons-material/PersonOutline";
import { Popover, PopoverTrigger } from "components/Popover/Popover";
import { HelpTooltipContent } from "components/HelpTooltip/HelpTooltip";
import { AvatarData } from "components/AvatarData/AvatarData";
import { Avatar } from "components/Avatar/Avatar";
import { ExternalAvatar } from "components/Avatar/Avatar";
export type WorkspaceError =
| "getBuildsError"
@ -176,7 +176,7 @@ export const WorkspaceTopbar = (props: WorkspaceProps) => {
}
avatar={
workspace.template_icon !== "" && (
<Avatar
<ExternalAvatar
src={workspace.template_icon}
variant="square"
fitImage

View File

@ -165,6 +165,25 @@ export const AllStates: Story = {
},
};
const icons = [
"/icon/code.svg",
"/icon/aws.svg",
"/icon/docker-white.svg",
"/icon/docker.svg",
"",
"/icon/doesntexist.svg",
];
export const Icons: Story = {
args: {
workspaces: allWorkspaces.map((workspace, i) => ({
...workspace,
template_icon: icons[i % icons.length],
})),
count: allWorkspaces.length,
},
};
export const OwnerHasNoWorkspaces: Story = {
args: {
workspaces: [],

View File

@ -17,7 +17,7 @@ import {
} from "components/TableLoader/TableLoader";
import { useClickableTableRow } from "hooks/useClickableTableRow";
import { AvatarData } from "components/AvatarData/AvatarData";
import { Avatar } from "components/Avatar/Avatar";
import { ExternalAvatar } from "components/Avatar/Avatar";
import { Stack } from "components/Stack/Stack";
import { LastUsed } from "pages/WorkspacesPage/LastUsed";
import { WorkspaceOutdatedTooltip } from "components/WorkspaceOutdatedTooltip/WorkspaceOutdatedTooltip";
@ -165,7 +165,7 @@ export const WorkspacesTable: FC<WorkspacesTableProps> = ({
}
subtitle={workspace.owner_name}
avatar={
<Avatar
<ExternalAvatar
src={workspace.template_icon}
variant={
workspace.template_icon ? "square" : undefined
@ -173,7 +173,7 @@ export const WorkspacesTable: FC<WorkspacesTableProps> = ({
fitImage={Boolean(workspace.template_icon)}
>
{workspace.name}
</Avatar>
</ExternalAvatar>
}
/>
</div>

View File

@ -2244,7 +2244,7 @@ export const MockTemplateExample: TypesGen.TemplateExample = {
description: "Get started with Linux development on AWS ECS.",
markdown:
"\n# aws-ecs\n\nThis is a sample template for running a Coder workspace on ECS. It assumes there\nis a pre-existing ECS cluster with EC2-based compute to host the workspace.\n\n## Architecture\n\nThis workspace is built using the following AWS resources:\n\n- Task definition - the container definition, includes the image, command, volume(s)\n- ECS service - manages the task definition\n\n## code-server\n\n`code-server` is installed via the `startup_script` argument in the `coder_agent`\nresource block. The `coder_app` resource is defined to access `code-server` through\nthe dashboard UI over `localhost:13337`.\n",
icon: "/icon/aws.png",
icon: "/icon/aws.svg",
tags: ["aws", "cloud"],
};
@ -2255,7 +2255,7 @@ export const MockTemplateExample2: TypesGen.TemplateExample = {
description: "Get started with Linux development on AWS EC2.",
markdown:
'\n# aws-linux\n\nTo get started, run `coder templates init`. When prompted, select this template.\nFollow the on-screen instructions to proceed.\n\n## Authentication\n\nThis template assumes that coderd is run in an environment that is authenticated\nwith AWS. For example, run `aws configure import` to import credentials on the\nsystem and user running coderd. For other ways to authenticate [consult the\nTerraform docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration).\n\n## Required permissions / policy\n\nThe following sample policy allows Coder to create EC2 instances and modify\ninstances provisioned by Coder:\n\n```json\n{\n "Version": "2012-10-17",\n "Statement": [\n {\n "Sid": "VisualEditor0",\n "Effect": "Allow",\n "Action": [\n "ec2:GetDefaultCreditSpecification",\n "ec2:DescribeIamInstanceProfileAssociations",\n "ec2:DescribeTags",\n "ec2:CreateTags",\n "ec2:RunInstances",\n "ec2:DescribeInstanceCreditSpecifications",\n "ec2:DescribeImages",\n "ec2:ModifyDefaultCreditSpecification",\n "ec2:DescribeVolumes"\n ],\n "Resource": "*"\n },\n {\n "Sid": "CoderResources",\n "Effect": "Allow",\n "Action": [\n "ec2:DescribeInstances",\n "ec2:DescribeInstanceAttribute",\n "ec2:UnmonitorInstances",\n "ec2:TerminateInstances",\n "ec2:StartInstances",\n "ec2:StopInstances",\n "ec2:DeleteTags",\n "ec2:MonitorInstances",\n "ec2:CreateTags",\n "ec2:RunInstances",\n "ec2:ModifyInstanceAttribute",\n "ec2:ModifyInstanceCreditSpecification"\n ],\n "Resource": "arn:aws:ec2:*:*:instance/*",\n "Condition": {\n "StringEquals": {\n "aws:ResourceTag/Coder_Provisioned": "true"\n }\n }\n }\n ]\n}\n```\n\n## code-server\n\n`code-server` is installed via the `startup_script` argument in the `coder_agent`\nresource block. The `coder_app` resource is defined to access `code-server` through\nthe dashboard UI over `localhost:13337`.\n',
icon: "/icon/aws.png",
icon: "/icon/aws.svg",
tags: ["aws", "cloud"],
};

View File

@ -1,9 +1,11 @@
import experimental from "./experimental";
import monaco from "./monaco";
import muiTheme from "./mui";
import { forDarkThemes } from "../externalImages";
export default {
...muiTheme,
experimental,
monaco,
externalImages: forDarkThemes,
};

View File

@ -1,9 +1,11 @@
import experimental from "./experimental";
import monaco from "./monaco";
import muiTheme from "./mui";
import { forDarkThemes } from "../externalImages";
export default {
...muiTheme,
experimental,
monaco,
externalImages: forDarkThemes,
};

View File

@ -0,0 +1,85 @@
import {
forDarkThemes,
forLightThemes,
getExternalImageStylesFromUrl,
parseImageParameters,
} from "./externalImages";
describe("externalImage parameters", () => {
test("default parameters", () => {
// Correctly selects default
const widgetsStyles = getExternalImageStylesFromUrl(
forDarkThemes,
"/icon/widgets.svg",
);
expect(widgetsStyles).toBe(forDarkThemes.monochrome);
// Allows overrides
const overrideStyles = getExternalImageStylesFromUrl(
forDarkThemes,
"/icon/widgets.svg?fullcolor",
);
expect(overrideStyles).toBe(forDarkThemes.fullcolor);
// Not actually a built-in
const someoneElsesWidgetsStyles = getExternalImageStylesFromUrl(
forDarkThemes,
"https://example.com/icon/widgets.svg",
);
expect(someoneElsesWidgetsStyles).toBeUndefined();
});
test("blackWithColor brightness", () => {
const tryCase = (params: string) =>
parseImageParameters(forDarkThemes, params);
const withDecimalValue = tryCase("?blackWithColor&brightness=1.5");
expect(withDecimalValue?.filter).toBe(
"invert(1) hue-rotate(180deg) brightness(1.5)",
);
const withPercentageValue = tryCase("?blackWithColor&brightness=150%");
expect(withPercentageValue?.filter).toBe(
"invert(1) hue-rotate(180deg) brightness(150%)",
);
// Sketchy `brightness` value will be ignored.
const niceTry = tryCase(
"?blackWithColor&brightness=</style><script>alert('leet hacking');</script>",
);
expect(niceTry?.filter).toBe("invert(1) hue-rotate(180deg)");
const withLightTheme = parseImageParameters(
forLightThemes,
"?blackWithColor&brightness=1.5",
);
expect(withLightTheme).toBeUndefined();
});
test("whiteWithColor brightness", () => {
const tryCase = (params: string) =>
parseImageParameters(forLightThemes, params);
const withDecimalValue = tryCase("?whiteWithColor&brightness=1.5");
expect(withDecimalValue?.filter).toBe(
"invert(1) hue-rotate(180deg) brightness(1.5)",
);
const withPercentageValue = tryCase("?whiteWithColor&brightness=150%");
expect(withPercentageValue?.filter).toBe(
"invert(1) hue-rotate(180deg) brightness(150%)",
);
// Sketchy `brightness` value will be ignored.
const niceTry = tryCase(
"?whiteWithColor&brightness=</style><script>alert('leet hacking');</script>",
);
expect(niceTry?.filter).toBe("invert(1) hue-rotate(180deg)");
const withDarkTheme = parseImageParameters(
forDarkThemes,
"?whiteWithColor&brightness=1.5",
);
expect(withDarkTheme).toBeUndefined();
});
});

View File

@ -0,0 +1,163 @@
import { type CSSObject } from "@emotion/react";
export type ExternalImageMode = keyof ExternalImageModeStyles;
export interface ExternalImageModeStyles {
/**
* monochrome icons will be flattened to a neutral, theme-appropriate color.
* eg. white, light gray, dark gray, black
*/
monochrome?: CSSObject;
/**
* @default
* fullcolor icons should look their best of any background, with distinct colors
* and good contrast. This is the default, and won't alter the image.
*/
fullcolor?: CSSObject;
/**
* whiteWithColor is useful for icons that are primarily white, or contain white text,
* which are hard to see or look incorrect on light backgrounds. This setting will apply
* a color-respecting inversion filter to turn white into black when appropriate to
* improve contrast.
* You can also specify a `brightness` level if your icon still doesn't look quite right.
* eg. /icon/aws.svg?blackWithColor&brightness=1.5
*/
whiteWithColor?: CSSObject;
/**
* blackWithColor is useful for icons that are primarily black, or contain black text,
* which are hard to see or look incorrect on dark backgrounds. This setting will apply
* a color-respecting inversion filter to turn black into white when appropriate to
* improve contrast.
* You can also specify a `brightness` level if your icon still doesn't look quite right.
* eg. /icon/aws.svg?blackWithColor&brightness=1.5
*/
blackWithColor?: CSSObject;
}
export const forDarkThemes: ExternalImageModeStyles = {
// brighten icons a little to make sure they have good contrast with the background
monochrome: { filter: "grayscale(100%) contrast(0%) brightness(250%)" },
// do nothing to full-color icons
fullcolor: undefined,
// white on a dark background ✅
whiteWithColor: undefined,
// black on a dark background 🆘: invert, and then correct colors
blackWithColor: { filter: "invert(1) hue-rotate(180deg)" },
};
export const forLightThemes: ExternalImageModeStyles = {
// darken icons a little to make sure they have good contrast with the background
monochrome: { filter: "grayscale(100%) contrast(0%) brightness(70%)" },
// do nothing to full-color icons
fullcolor: undefined,
// black on a dark background 🆘: invert, and then correct colors
whiteWithColor: { filter: "invert(1) hue-rotate(180deg)" },
// black on a light background ✅
blackWithColor: undefined,
};
// multiplier matches the beginning of the string (^), a number, optionally followed
// followed by a decimal portion, optionally followed by a percent symbol, and the
// end of the string ($).
const multiplier = /^\d+(\.\d+)?%?$/;
/**
* Used with `whiteWithColor` and `blackWithColor` to allow for finer tuning
*/
const parseInvertFilterParameters = (
params: URLSearchParams,
baseStyles?: CSSObject,
) => {
// Only apply additional styles if the current theme supports this mode
if (!baseStyles) {
return;
}
let extraStyles: CSSObject | undefined;
const brightness = params.get("brightness");
if (multiplier.test(brightness!)) {
let filter = baseStyles.filter ?? "";
filter += ` brightness(${brightness})`;
extraStyles = { ...extraStyles, filter };
}
if (!extraStyles) {
return baseStyles;
}
return {
...baseStyles,
...extraStyles,
};
};
export function parseImageParameters(
modes: ExternalImageModeStyles,
searchString: string,
): CSSObject | undefined {
const params = new URLSearchParams(searchString);
let styles: CSSObject | undefined = modes.fullcolor;
if (params.has("monochrome")) {
styles = modes.monochrome;
} else if (params.has("whiteWithColor")) {
styles = parseInvertFilterParameters(params, modes.whiteWithColor);
} else if (params.has("blackWithColor")) {
styles = parseInvertFilterParameters(params, modes.blackWithColor);
}
return styles;
}
export function getExternalImageStylesFromUrl(
modes: ExternalImageModeStyles,
urlString?: string,
) {
if (!urlString) {
return undefined;
}
const url = new URL(urlString, location.origin);
if (url.search) {
return parseImageParameters(modes, url.search);
}
if (
url.origin === location.origin &&
defaultParametersForBuiltinIcons.has(url.pathname)
) {
return parseImageParameters(
modes,
defaultParametersForBuiltinIcons.get(url.pathname)!,
);
}
return undefined;
}
/**
* defaultModeForBuiltinIcons contains modes for all of our built-in icons that
* don't look their best in all of our themes with the default fullcolor mode.
*/
export const defaultParametersForBuiltinIcons = new Map<string, string>([
["/icon/apple-black.svg", "monochrome"],
["/icon/aws.png", "whiteWithColor&brightness=1.5"],
["/icon/aws.svg", "blackWithColor&brightness=1.5"],
["/icon/aws-monochrome.svg", "monochrome"],
["/icon/coder.svg", "monochrome"],
["/icon/container.svg", "monochrome"],
["/icon/database.svg", "monochrome"],
["/icon/docker-white.svg", "monochrome"],
["/icon/folder.svg", "monochrome"],
["/icon/github.svg", "monochrome"],
["/icon/image.svg", "monochrome"],
["/icon/jupyter.svg", "blackWithColor"],
["/icon/kasmvnc.svg", "whiteWithColor"],
["/icon/memory.svg", "monochrome"],
["/icon/rust.svg", "monochrome"],
["/icon/terminal.svg", "monochrome"],
["/icon/widgets.svg", "monochrome"],
]);

View File

@ -5,6 +5,7 @@
"apple-grey.svg",
"aws-dark.svg",
"aws-light.svg",
"aws-monochrome.svg",
"aws.png",
"aws.svg",
"azure-devops.svg",
@ -53,6 +54,7 @@
"memory.svg",
"microsoft.svg",
"node.svg",
"nodejs.svg",
"nomad.svg",
"novnc.svg",
"okta.svg",

View File

@ -4,10 +4,12 @@ import dark from "./dark";
import darkBlue from "./darkBlue";
import light from "./light";
import type { NewTheme } from "./experimental";
import type { ExternalImageModeStyles } from "./externalImages";
export interface Theme extends MuiTheme {
experimental: NewTheme;
monaco: monaco.editor.IStandaloneThemeData;
externalImages: ExternalImageModeStyles;
}
export const DEFAULT_THEME = "dark";

View File

@ -1,9 +1,11 @@
import experimental from "./experimental";
import monaco from "./monaco";
import muiTheme from "./mui";
import { forLightThemes } from "../externalImages";
export default {
...muiTheme,
experimental,
monaco,
externalImages: forLightThemes,
};

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 304 182" style="enable-background:new 0 0 304 182;" xml:space="preserve">
<style type="text/css">
.st0{fill:#fff;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#fff;}
</style>
<g>
<path class="st0" d="M86.4,66.4c0,3.7,0.4,6.7,1.1,8.9c0.8,2.2,1.8,4.6,3.2,7.2c0.5,0.8,0.7,1.6,0.7,2.3c0,1-0.6,2-1.9,3l-6.3,4.2
c-0.9,0.6-1.8,0.9-2.6,0.9c-1,0-2-0.5-3-1.4C76.2,90,75,88.4,74,86.8c-1-1.7-2-3.6-3.1-5.9c-7.8,9.2-17.6,13.8-29.4,13.8
c-8.4,0-15.1-2.4-20-7.2c-4.9-4.8-7.4-11.2-7.4-19.2c0-8.5,3-15.4,9.1-20.6c6.1-5.2,14.2-7.8,24.5-7.8c3.4,0,6.9,0.3,10.6,0.8
c3.7,0.5,7.5,1.3,11.5,2.2v-7.3c0-7.6-1.6-12.9-4.7-16c-3.2-3.1-8.6-4.6-16.3-4.6c-3.5,0-7.1,0.4-10.8,1.3c-3.7,0.9-7.3,2-10.8,3.4
c-1.6,0.7-2.8,1.1-3.5,1.3c-0.7,0.2-1.2,0.3-1.6,0.3c-1.4,0-2.1-1-2.1-3.1v-4.9c0-1.6,0.2-2.8,0.7-3.5c0.5-0.7,1.4-1.4,2.8-2.1
c3.5-1.8,7.7-3.3,12.6-4.5c4.9-1.3,10.1-1.9,15.6-1.9c11.9,0,20.6,2.7,26.2,8.1c5.5,5.4,8.3,13.6,8.3,24.6V66.4z M45.8,81.6
c3.3,0,6.7-0.6,10.3-1.8c3.6-1.2,6.8-3.4,9.5-6.4c1.6-1.9,2.8-4,3.4-6.4c0.6-2.4,1-5.3,1-8.7v-4.2c-2.9-0.7-6-1.3-9.2-1.7
c-3.2-0.4-6.3-0.6-9.4-0.6c-6.7,0-11.6,1.3-14.9,4c-3.3,2.7-4.9,6.5-4.9,11.5c0,4.7,1.2,8.2,3.7,10.6
C37.7,80.4,41.2,81.6,45.8,81.6z M126.1,92.4c-1.8,0-3-0.3-3.8-1c-0.8-0.6-1.5-2-2.1-3.9L96.7,10.2c-0.6-2-0.9-3.3-0.9-4
c0-1.6,0.8-2.5,2.4-2.5h9.8c1.9,0,3.2,0.3,3.9,1c0.8,0.6,1.4,2,2,3.9l16.8,66.2l15.6-66.2c0.5-2,1.1-3.3,1.9-3.9c0.8-0.6,2.2-1,4-1
h8c1.9,0,3.2,0.3,4,1c0.8,0.6,1.5,2,1.9,3.9l15.8,67l17.3-67c0.6-2,1.3-3.3,2-3.9c0.8-0.6,2.1-1,3.9-1h9.3c1.6,0,2.5,0.8,2.5,2.5
c0,0.5-0.1,1-0.2,1.6c-0.1,0.6-0.3,1.4-0.7,2.5l-24.1,77.3c-0.6,2-1.3,3.3-2.1,3.9c-0.8,0.6-2.1,1-3.8,1h-8.6c-1.9,0-3.2-0.3-4-1
c-0.8-0.7-1.5-2-1.9-4L156,23l-15.4,64.4c-0.5,2-1.1,3.3-1.9,4c-0.8,0.7-2.2,1-4,1H126.1z M254.6,95.1c-5.2,0-10.4-0.6-15.4-1.8
c-5-1.2-8.9-2.5-11.5-4c-1.6-0.9-2.7-1.9-3.1-2.8c-0.4-0.9-0.6-1.9-0.6-2.8v-5.1c0-2.1,0.8-3.1,2.3-3.1c0.6,0,1.2,0.1,1.8,0.3
c0.6,0.2,1.5,0.6,2.5,1c3.4,1.5,7.1,2.7,11,3.5c4,0.8,7.9,1.2,11.9,1.2c6.3,0,11.2-1.1,14.6-3.3c3.4-2.2,5.2-5.4,5.2-9.5
c0-2.8-0.9-5.1-2.7-7c-1.8-1.9-5.2-3.6-10.1-5.2L246,52c-7.3-2.3-12.7-5.7-16-10.2c-3.3-4.4-5-9.3-5-14.5c0-4.2,0.9-7.9,2.7-11.1
c1.8-3.2,4.2-6,7.2-8.2c3-2.3,6.4-4,10.4-5.2c4-1.2,8.2-1.7,12.6-1.7c2.2,0,4.5,0.1,6.7,0.4c2.3,0.3,4.4,0.7,6.5,1.1
c2,0.5,3.9,1,5.7,1.6c1.8,0.6,3.2,1.2,4.2,1.8c1.4,0.8,2.4,1.6,3,2.5c0.6,0.8,0.9,1.9,0.9,3.3v4.7c0,2.1-0.8,3.2-2.3,3.2
c-0.8,0-2.1-0.4-3.8-1.2c-5.7-2.6-12.1-3.9-19.2-3.9c-5.7,0-10.2,0.9-13.3,2.8c-3.1,1.9-4.7,4.8-4.7,8.9c0,2.8,1,5.2,3,7.1
c2,1.9,5.7,3.8,11,5.5l14.2,4.5c7.2,2.3,12.4,5.5,15.5,9.6c3.1,4.1,4.6,8.8,4.6,14c0,4.3-0.9,8.2-2.6,11.6
c-1.8,3.4-4.2,6.4-7.3,8.8c-3.1,2.5-6.8,4.3-11.1,5.6C264.4,94.4,259.7,95.1,254.6,95.1z"/>
<g>
<path class="st1" d="M273.5,143.7c-32.9,24.3-80.7,37.2-121.8,37.2c-57.6,0-109.5-21.3-148.7-56.7c-3.1-2.8-0.3-6.6,3.4-4.4
c42.4,24.6,94.7,39.5,148.8,39.5c36.5,0,76.6-7.6,113.5-23.2C274.2,133.6,278.9,139.7,273.5,143.7z"/>
<path class="st1" d="M287.2,128.1c-4.2-5.4-27.8-2.6-38.5-1.3c-3.2,0.4-3.7-2.4-0.8-4.5c18.8-13.2,49.7-9.4,53.3-5
c3.6,4.5-1,35.4-18.6,50.2c-2.7,2.3-5.3,1.1-4.1-1.9C282.5,155.7,291.4,133.4,287.2,128.1z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -1,8 +1,8 @@
<svg width="66" height="48" viewBox="0 0 66 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M64.3029 20.8302C62.9894 20.8302 62.1144 20.0449 62.1144 18.4331V9.17517C62.1144 3.26504 59.7268 0 53.5592 0H50.6941V6.24078H51.5697C53.9968 6.24078 55.1508 7.60467 55.1508 10.0431V18.2264C55.1508 21.7807 56.1853 23.2273 58.4535 23.9713C56.1853 24.6739 55.1508 26.1617 55.1508 29.716C55.1508 31.7412 55.1508 33.7663 55.1508 35.7916C55.1508 37.4861 55.1508 39.1393 54.7131 40.8337C54.2754 42.4044 53.5592 43.8922 52.5644 45.1733C52.0073 45.9174 51.3707 46.5373 50.6545 47.116V47.9425H53.5193C59.687 47.9425 62.0746 44.6774 62.0746 38.7672V29.5094C62.0746 27.8562 62.9103 27.1123 64.2634 27.1123H65.8944V20.8714H64.3029V20.8302Z" fill="#D9D9D9"/>
<path d="M44.8049 9.42443H35.9712C35.7722 9.42443 35.6131 9.25912 35.6131 9.05247V8.34987C35.6131 8.14322 35.7722 7.97791 35.9712 7.97791H44.8447C45.0436 7.97791 45.2028 8.14322 45.2028 8.34987V9.05247C45.2028 9.25912 45.0038 9.42443 44.8049 9.42443Z" fill="#D9D9D9"/>
<path d="M46.3171 18.3513H39.871C39.672 18.3513 39.5128 18.1859 39.5128 17.9792V17.2767C39.5128 17.0701 39.672 16.9047 39.871 16.9047H46.3171C46.5161 16.9047 46.6752 17.0701 46.6752 17.2767V17.9792C46.6752 18.1446 46.5161 18.3513 46.3171 18.3513Z" fill="#D9D9D9"/>
<path d="M48.8636 13.8879H35.9712C35.7722 13.8879 35.6131 13.7226 35.6131 13.5159V12.8133C35.6131 12.6067 35.7722 12.4413 35.9712 12.4413H48.8237C49.0228 12.4413 49.182 12.6067 49.182 12.8133V13.5159C49.182 13.6812 49.0626 13.8879 48.8636 13.8879Z" fill="#D9D9D9"/>
<path d="M25.7449 11.4483C26.6203 11.4483 27.4958 11.531 28.3313 11.7377V10.0431C28.3313 7.64602 29.5251 6.24078 31.9126 6.24078H32.7879V0H29.923C23.7552 0 21.3679 3.26504 21.3679 9.17517V12.2336C22.7605 11.7377 24.2329 11.4483 25.7449 11.4483Z" fill="#D9D9D9"/>
<path d="M51.5695 33.9308C50.9329 28.6819 47.0333 24.3009 42.0196 23.3089C40.6269 23.0197 39.2342 22.9783 37.8813 23.2263C37.8415 23.2263 37.8415 23.1849 37.8018 23.1849C35.6132 18.4321 30.9179 15.291 25.8246 15.291C20.7313 15.291 16.0757 18.3494 13.8474 23.1023C13.8076 23.1023 13.8076 23.1437 13.7678 23.1437C12.3353 22.9783 10.9028 23.0609 9.47035 23.433C4.5362 24.6728 0.795835 28.9711 0.119377 34.1786C0.039787 34.7159 0 35.2532 0 35.7492C0 37.3196 1.03457 38.7662 2.54664 38.9729C4.41683 39.2623 6.04827 37.7743 6.00848 35.8732C6.00848 35.5838 6.00848 35.2532 6.04827 34.9639C6.36659 32.3188 8.31638 30.087 10.863 29.467C11.6589 29.2604 12.4547 29.2191 13.2107 29.3432C15.638 29.6738 18.0255 28.3925 19.06 26.1607C19.8161 24.5075 21.0098 23.0609 22.6015 22.2757C24.3522 21.4077 26.3418 21.2838 28.1723 21.9452C30.0822 22.6477 31.5146 24.1355 32.3901 25.9953C33.3053 27.814 33.743 29.0951 35.6928 29.3432C36.4886 29.467 38.7169 29.4257 39.5526 29.3844C41.184 29.3844 42.8154 29.963 43.9694 31.1616C44.7254 31.9881 45.2825 33.0214 45.5213 34.1786C45.8793 36.0385 45.4417 37.8983 44.3673 39.3035C43.6112 40.2954 42.5767 41.0394 41.4227 41.37C40.8656 41.5354 40.3085 41.5766 39.7514 41.5766C39.4332 41.5766 38.9955 41.5766 38.4782 41.5766C36.8866 41.5766 33.5043 41.5766 30.9576 41.5766C29.2069 41.5766 27.8141 40.1302 27.8141 38.3116V26.2019C27.8141 25.7061 27.4162 25.2928 26.9387 25.2928H25.7052C23.2778 25.334 21.3281 28.1446 21.3281 31.1202C21.3281 34.096 21.3281 41.99 21.3281 41.99C21.3281 45.2137 23.8349 47.8175 26.9387 47.8175C26.9387 47.8175 40.7464 47.7761 40.9452 47.7761C44.1285 47.4454 47.0731 45.751 49.0626 43.1472C51.0522 40.6261 51.9674 37.3196 51.5695 33.9308Z" fill="#D9D9D9"/>
<svg width="66" height="48" viewBox="0 0 66 48" fill="#fff" xmlns="http://www.w3.org/2000/svg">
<path d="M64.3029 20.8302C62.9894 20.8302 62.1144 20.0449 62.1144 18.4331V9.17517C62.1144 3.26504 59.7268 0 53.5592 0H50.6941V6.24078H51.5697C53.9968 6.24078 55.1508 7.60467 55.1508 10.0431V18.2264C55.1508 21.7807 56.1853 23.2273 58.4535 23.9713C56.1853 24.6739 55.1508 26.1617 55.1508 29.716C55.1508 31.7412 55.1508 33.7663 55.1508 35.7916C55.1508 37.4861 55.1508 39.1393 54.7131 40.8337C54.2754 42.4044 53.5592 43.8922 52.5644 45.1733C52.0073 45.9174 51.3707 46.5373 50.6545 47.116V47.9425H53.5193C59.687 47.9425 62.0746 44.6774 62.0746 38.7672V29.5094C62.0746 27.8562 62.9103 27.1123 64.2634 27.1123H65.8944V20.8714H64.3029V20.8302Z" />
<path d="M44.8049 9.42443H35.9712C35.7722 9.42443 35.6131 9.25912 35.6131 9.05247V8.34987C35.6131 8.14322 35.7722 7.97791 35.9712 7.97791H44.8447C45.0436 7.97791 45.2028 8.14322 45.2028 8.34987V9.05247C45.2028 9.25912 45.0038 9.42443 44.8049 9.42443Z" />
<path d="M46.3171 18.3513H39.871C39.672 18.3513 39.5128 18.1859 39.5128 17.9792V17.2767C39.5128 17.0701 39.672 16.9047 39.871 16.9047H46.3171C46.5161 16.9047 46.6752 17.0701 46.6752 17.2767V17.9792C46.6752 18.1446 46.5161 18.3513 46.3171 18.3513Z" />
<path d="M48.8636 13.8879H35.9712C35.7722 13.8879 35.6131 13.7226 35.6131 13.5159V12.8133C35.6131 12.6067 35.7722 12.4413 35.9712 12.4413H48.8237C49.0228 12.4413 49.182 12.6067 49.182 12.8133V13.5159C49.182 13.6812 49.0626 13.8879 48.8636 13.8879Z" />
<path d="M25.7449 11.4483C26.6203 11.4483 27.4958 11.531 28.3313 11.7377V10.0431C28.3313 7.64602 29.5251 6.24078 31.9126 6.24078H32.7879V0H29.923C23.7552 0 21.3679 3.26504 21.3679 9.17517V12.2336C22.7605 11.7377 24.2329 11.4483 25.7449 11.4483Z" />
<path d="M51.5695 33.9308C50.9329 28.6819 47.0333 24.3009 42.0196 23.3089C40.6269 23.0197 39.2342 22.9783 37.8813 23.2263C37.8415 23.2263 37.8415 23.1849 37.8018 23.1849C35.6132 18.4321 30.9179 15.291 25.8246 15.291C20.7313 15.291 16.0757 18.3494 13.8474 23.1023C13.8076 23.1023 13.8076 23.1437 13.7678 23.1437C12.3353 22.9783 10.9028 23.0609 9.47035 23.433C4.5362 24.6728 0.795835 28.9711 0.119377 34.1786C0.039787 34.7159 0 35.2532 0 35.7492C0 37.3196 1.03457 38.7662 2.54664 38.9729C4.41683 39.2623 6.04827 37.7743 6.00848 35.8732C6.00848 35.5838 6.00848 35.2532 6.04827 34.9639C6.36659 32.3188 8.31638 30.087 10.863 29.467C11.6589 29.2604 12.4547 29.2191 13.2107 29.3432C15.638 29.6738 18.0255 28.3925 19.06 26.1607C19.8161 24.5075 21.0098 23.0609 22.6015 22.2757C24.3522 21.4077 26.3418 21.2838 28.1723 21.9452C30.0822 22.6477 31.5146 24.1355 32.3901 25.9953C33.3053 27.814 33.743 29.0951 35.6928 29.3432C36.4886 29.467 38.7169 29.4257 39.5526 29.3844C41.184 29.3844 42.8154 29.963 43.9694 31.1616C44.7254 31.9881 45.2825 33.0214 45.5213 34.1786C45.8793 36.0385 45.4417 37.8983 44.3673 39.3035C43.6112 40.2954 42.5767 41.0394 41.4227 41.37C40.8656 41.5354 40.3085 41.5766 39.7514 41.5766C39.4332 41.5766 38.9955 41.5766 38.4782 41.5766C36.8866 41.5766 33.5043 41.5766 30.9576 41.5766C29.2069 41.5766 27.8141 40.1302 27.8141 38.3116V26.2019C27.8141 25.7061 27.4162 25.2928 26.9387 25.2928H25.7052C23.2778 25.334 21.3281 28.1446 21.3281 31.1202C21.3281 34.096 21.3281 41.99 21.3281 41.99C21.3281 45.2137 23.8349 47.8175 26.9387 47.8175C26.9387 47.8175 40.7464 47.7761 40.9452 47.7761C44.1285 47.4454 47.0731 45.751 49.0626 43.1472C51.0522 40.6261 51.9674 37.3196 51.5695 33.9308Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -1,8 +1,86 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="108.76" height="144.13" viewBox="0 0 108.758 144.133">
<path fill="#D70751" d="M60.969 47.645c-1.494.02.281.768 2.232 1.069.541-.422 1.027-.846 1.463-1.26-1.213.297-2.449.304-3.695.191m8.017-1.999c.893-1.229 1.541-2.573 1.77-3.963-.201.99-.736 1.845-1.244 2.749-2.793 1.759-.264-1.044-.002-2.111-3.002 3.783-.414 2.268-.524 3.325m2.963-7.704c.182-2.691-.529-1.839-.768-.814.278.146.499 1.898.768.814M55.301 1.163c.798.142 1.724.252 1.591.443.876-.193 1.073-.367-1.591-.443m1.592.443-.561.117.523-.048.038-.069"/>
<path fill="#D70751" d="M81.762 38.962c.09 2.416-.705 3.59-1.424 5.666l-1.293.643c-1.057 2.054.105 1.304-.652 2.937-1.652 1.467-5.006 4.589-6.08 4.875-.785-.017.531-.926.703-1.281-2.209 1.516-1.773 2.276-5.152 3.199l-.098-.221c-8.33 3.92-19.902-3.847-19.75-14.443-.088.672-.253.504-.437.774-.43-5.451 2.518-10.926 7.49-13.165 4.863-2.406 10.564-1.42 14.045 1.829-1.912-2.506-5.721-5.163-10.232-4.917-4.421.072-8.558 2.881-9.938 5.932-2.264 1.425-2.528 5.496-3.514 6.242-1.329 9.76 2.497 13.975 8.97 18.936 1.016.686.286.791.422 1.313-2.15-1.006-4.118-2.526-5.738-4.387.86 1.257 1.787 2.479 2.986 3.439-2.029-.685-4.738-4.913-5.527-5.085 3.495 6.258 14.178 10.975 19.775 8.634-2.59.096-5.879.053-8.787-1.022-1.225-.629-2.884-1.93-2.587-2.173 7.636 2.851 15.522 2.158 22.128-3.137 1.682-1.31 3.518-3.537 4.049-3.567-.799 1.202.137.578-.477 1.639 1.672-2.701-.729-1.1 1.73-4.664l.908 1.25c-.34-2.244 2.785-4.966 2.467-8.512.717-1.084.799 1.168.039 3.662 1.055-2.767.279-3.212.549-5.496.291.768.678 1.583.875 2.394-.688-2.675.703-4.503 1.049-6.058-.342-.15-1.061 1.182-1.227-1.976.025-1.372.383-.719.52-1.057-.268-.155-.975-1.207-1.404-3.224.309-.475.832 1.229 1.256 1.298-.273-1.603-.742-2.826-.762-4.057-1.24-2.59-.439.346-1.443-1.112-1.32-4.114 1.094-.955 1.258-2.823 1.998 2.895 3.137 7.385 3.662 9.244-.4-2.267-1.045-4.464-1.834-6.589.609.257-.979-4.663.791-1.405C87.189 15.552 81 9.062 75.305 6.018c.695.637 1.574 1.437 1.26 1.563-2.834-1.685-2.336-1.818-2.742-2.53-2.305-.939-2.459.077-3.984.002-4.35-2.308-5.188-2.063-9.191-3.507l.182.852c-2.881-.96-3.357.362-6.47.002-.189-.147.998-.536 1.976-.677-2.786.368-2.656-.55-5.382.101.671-.471 1.383-.784 2.099-1.184-2.271.138-5.424 1.322-4.451.244-3.705 1.654-10.286 3.975-13.979 7.438l-.116-.776c-1.692 2.031-7.379 6.066-7.832 8.699l-.453.105c-.879 1.491-1.45 3.18-2.148 4.713-1.151 1.963-1.688.756-1.524 1.064-2.265 4.592-3.392 8.45-4.363 11.616.692 1.035.017 6.232.278 10.391-1.136 20.544 14.418 40.489 31.42 45.093 2.492.893 6.197.861 9.349.949-3.718-1.064-4.198-.563-7.822-1.826-2.613-1.232-3.185-2.637-5.037-4.244l.733 1.295c-3.63-1.285-2.111-1.59-5.065-2.525l.783-1.021c-1.177-.09-3.117-1.982-3.647-3.033l-1.288.051c-1.546-1.906-2.371-3.283-2.31-4.35l-.416.742c-.471-.809-5.691-7.158-2.983-5.68-.503-.458-1.172-.747-1.897-2.066l.551-.629c-1.301-1.677-2.398-3.826-2.314-4.542.695.938 1.177 1.114 1.655 1.275-3.291-8.164-3.476-.449-5.967-8.31l.526-.042c-.403-.611-.65-1.27-.974-1.919l.23-2.285c-2.368-2.736-.662-11.645-.319-16.53.235-1.986 1.977-4.101 3.3-7.418l-.806-.138c1.542-2.688 8.802-10.799 12.166-10.383 1.629-2.046-.324-.008-.643-.522 3.579-3.703 4.704-2.616 7.119-3.283 2.603-1.545-2.235.604-1.001-.589 4.503-1.149 3.19-2.614 9.063-3.197.62.352-1.437.544-1.953 1.001 3.75-1.836 11.869-1.417 17.145 1.018 6.117 2.861 12.994 11.314 13.266 19.267l.309.083c-.156 3.162.484 6.819-.627 10.177l.751-1.591"/>
<path fill="#D70751" d="m44.658 49.695-.211 1.047c.983 1.335 1.763 2.781 3.016 3.821-.902-1.759-1.571-2.486-2.805-4.868m2.321-.09c-.52-.576-.826-1.268-1.172-1.956.33 1.211 1.006 2.252 1.633 3.312l-.461-1.356m41.084-8.93-.219.552c-.402 2.858-1.273 5.686-2.605 8.309 1.472-2.767 2.421-5.794 2.824-8.861M55.598.446C56.607.077 58.08.243 59.154 0c-1.398.117-2.789.187-4.162.362l.606.084M20.127 19.308c.233 2.154-1.62 2.991.41 1.569 1.09-2.454-.424-.677-.41-1.569m-2.388 9.974c.469-1.437.553-2.299.732-3.132-1.293 1.654-.596 2.007-.732 3.132"/>
<path d="M13.437 125.51c-.045.047-.045 7.506-.138 9.453-.092 1.574-.232 4.957-3.568 4.957-3.429 0-4.263-3.939-4.541-5.652-.324-1.9-.324-3.477-.324-4.17 0-2.225.139-8.436 5.375-8.436 1.576 0 2.456.465 3.151.834l.045 3.02zM0 130.98c0 13.066 6.951 13.066 7.97 13.066 2.873 0 4.727-1.576 5.514-4.309l.093 4.123c.881-.047 1.761-.139 3.197-.139.51 0 .926 0 1.298.047.371 0 .741.045 1.158.092-.741-1.482-1.297-4.818-1.297-12.049 0-7.043 0-18.951.602-22.566-1.667.789-3.105 1.299-6.256 1.576 1.251 1.344 1.251 2.039 1.251 8.154-.879-.277-1.992-.602-3.892-.602-8.294 0-9.638 7.23-9.638 12.61m25.13-2.373c.047-3.846.835-7.275 4.124-7.275 3.615 0 3.891 3.984 3.799 7.275H25.13zm12.51.46c0-5.422-1.065-10.752-7.923-10.752-9.452 0-9.452 10.475-9.452 12.697 0 9.406 4.216 13.113 11.306 13.113 3.149 0 4.68-.461 5.514-.695-.046-1.668.185-2.734.465-4.17-.975.604-2.226 1.391-5.006 1.391-7.229 0-7.322-6.582-7.322-8.852H37.55l.09-2.74m15.075 2.008c0 4.309-.787 10.102-6.162 10.102-.742 0-1.668-.141-2.27-.279-.093-1.668-.093-4.541-.093-7.877 0-3.986.416-6.068.742-7.09.972-3.289 3.15-3.334 3.566-3.334 3.522 0 4.217 4.86 4.217 8.48zm-13.298 5.05c0 3.43 0 5.375-.556 6.857 1.9.742 4.262 1.158 7.09 1.158 1.807 0 7.043 0 9.869-5.791 1.344-2.688 1.807-6.303 1.807-9.037 0-1.668-.186-5.328-1.529-7.646-1.296-2.176-3.382-3.289-5.605-3.289-4.449 0-5.746 3.707-6.44 5.607 0-2.363.045-10.611.415-14.828-3.011 1.391-4.866 1.621-6.857 1.807 1.807.74 1.807 3.801 1.807 13.764v11.397m27.117 7.741c-.928-.139-1.578-.232-2.922-.232-1.48 0-2.502.094-3.566.232.463-.881.648-1.299.787-4.309.186-4.125.232-15.154-.092-17.471-.232-1.762-.648-2.039-1.297-2.502 3.799-.371 4.865-.648 6.625-1.482-.369 2.037-.418 3.059-.418 6.162-.091 15.98-.138 17.7.883 19.6m14.838-13.118c-.092 2.92-.139 4.959-.928 6.58-.973 2.086-2.594 2.688-3.799 2.688-2.783 0-3.383-2.316-3.383-4.586 0-4.355 3.893-4.682 5.652-4.682h2.458zm-12.744 5.7c0 2.92.881 5.838 3.477 7.09 1.158.51 2.316.51 2.688.51 4.264 0 5.699-3.152 6.58-5.098-.047 2.039 0 3.289.139 4.912.834-.047 1.668-.139 3.059-.139.787 0 1.529.092 2.316.139-.51-.787-.787-1.252-.928-3.059-.092-1.76-.092-3.521-.092-5.977l.047-9.453c0-3.523-.928-6.998-7.879-6.998-4.586 0-7.273 1.391-8.617 2.086.557 1.02 1.02 1.898 1.436 3.893 1.809-1.576 4.172-2.41 6.58-2.41 3.848 0 3.848 2.549 3.848 6.162-.881-.045-1.623-.137-2.875-.137-5.887.01-9.779 2.28-9.779 8.49m39.431 2.819c.047 1.576.047 3.244.695 4.588-1.021-.092-1.623-.232-3.521-.232-1.113 0-1.715.094-2.596.232.184-.602.279-.834.371-1.623.139-1.064.232-4.633.232-5.885v-5.004c0-2.178 0-5.33-.141-6.441-.092-.787-.322-2.918-3.012-2.918-2.641 0-3.521 1.945-3.846 3.521-.369 1.621-.369 3.383-.369 10.24.045 5.932.045 6.486.508 8.109-.787-.092-1.76-.184-3.15-.184-1.113 0-1.854.045-2.779.184.324-.742.51-1.113.602-3.707.094-2.549.279-15.061-.141-18.025-.23-1.809-.695-2.225-1.203-2.688 3.754-.186 4.957-.789 6.117-1.389v4.91c.555-1.438 1.713-4.635 6.348-4.635 5.793 0 5.838 4.217 5.885 6.996v13.928"/>
<path fill="#D70751" d="m66.926 111.53-3.838 3.836-3.836-3.836 3.836-3.836 3.838 3.84"/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 10.0, SVG Export Plug-In . SVG Version: 3.0.0 Build 77) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
<!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/">
<!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
<!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/">
<!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/">
<!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/">
<!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/">
<!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/">
<!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/">
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg
xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;" i:viewOrigin="262 450" i:rulerOrigin="0 0" i:pageBounds="0 792 612 0"
xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
width="87.041" height="108.445" viewBox="0 0 87.041 108.445" overflow="visible" enable-background="new 0 0 87.041 108.445"
xml:space="preserve">
<metadata>
<variableSets xmlns="&ns_vars;">
<variableSet varSetName="binding1" locked="none">
<variables></variables>
<v:sampleDataSets xmlns="&ns_custom;" xmlns:v="&ns_vars;"></v:sampleDataSets>
</variableSet>
</variableSets>
<sfw xmlns="&ns_sfw;">
<slices></slices>
<sliceSourceBounds y="341.555" x="262" width="87.041" height="108.445" bottomLeftOrigin="true"></sliceSourceBounds>
</sfw>
</metadata>
<g id="Layer_1" i:layer="yes" i:dimmedPercent="50" i:rgbTrio="#4F008000FFFF">
<g>
<path i:knockout="Off" fill="#A80030" d="M51.986,57.297c-1.797,0.025,0.34,0.926,2.686,1.287
c0.648-0.506,1.236-1.018,1.76-1.516C54.971,57.426,53.484,57.434,51.986,57.297"/>
<path i:knockout="Off" fill="#A80030" d="M61.631,54.893c1.07-1.477,1.85-3.094,2.125-4.766c-0.24,1.192-0.887,2.221-1.496,3.307
c-3.359,2.115-0.316-1.256-0.002-2.537C58.646,55.443,61.762,53.623,61.631,54.893"/>
<path i:knockout="Off" fill="#A80030" d="M65.191,45.629c0.217-3.236-0.637-2.213-0.924-0.978
C64.602,44.825,64.867,46.932,65.191,45.629"/>
<path i:knockout="Off" fill="#A80030" d="M45.172,1.399c0.959,0.172,2.072,0.304,1.916,0.533
C48.137,1.702,48.375,1.49,45.172,1.399"/>
<path i:knockout="Off" fill="#A80030" d="M47.088,1.932l-0.678,0.14l0.631-0.056L47.088,1.932"/>
<path i:knockout="Off" fill="#A80030" d="M76.992,46.856c0.107,2.906-0.85,4.316-1.713,6.812l-1.553,0.776
c-1.271,2.468,0.123,1.567-0.787,3.53c-1.984,1.764-6.021,5.52-7.313,5.863c-0.943-0.021,0.639-1.113,0.846-1.541
c-2.656,1.824-2.131,2.738-6.193,3.846l-0.119-0.264c-10.018,4.713-23.934-4.627-23.751-17.371
c-0.107,0.809-0.304,0.607-0.526,0.934c-0.517-6.557,3.028-13.143,9.007-15.832c5.848-2.895,12.704-1.707,16.893,2.197
c-2.301-3.014-6.881-6.209-12.309-5.91c-5.317,0.084-10.291,3.463-11.951,7.131c-2.724,1.715-3.04,6.611-4.227,7.507
C31.699,56.271,36.3,61.342,44.083,67.307c1.225,0.826,0.345,0.951,0.511,1.58c-2.586-1.211-4.954-3.039-6.901-5.277
c1.033,1.512,2.148,2.982,3.589,4.137c-2.438-0.826-5.695-5.908-6.646-6.115c4.203,7.525,17.052,13.197,23.78,10.383
c-3.113,0.115-7.068,0.064-10.566-1.229c-1.469-0.756-3.467-2.322-3.11-2.615c9.182,3.43,18.667,2.598,26.612-3.771
c2.021-1.574,4.229-4.252,4.867-4.289c-0.961,1.445,0.164,0.695-0.574,1.971c2.014-3.248-0.875-1.322,2.082-5.609l1.092,1.504
c-0.406-2.696,3.348-5.97,2.967-10.234c0.861-1.304,0.961,1.403,0.047,4.403c1.268-3.328,0.334-3.863,0.66-6.609
c0.352,0.923,0.814,1.904,1.051,2.878c-0.826-3.216,0.848-5.416,1.262-7.285c-0.408-0.181-1.275,1.422-1.473-2.377
c0.029-1.65,0.459-0.865,0.625-1.271c-0.324-0.186-1.174-1.451-1.691-3.877c0.375-0.57,1.002,1.478,1.512,1.562
c-0.328-1.929-0.893-3.4-0.916-4.88c-1.49-3.114-0.527,0.415-1.736-1.337c-1.586-4.947,1.316-1.148,1.512-3.396
c2.404,3.483,3.775,8.881,4.404,11.117c-0.48-2.726-1.256-5.367-2.203-7.922c0.73,0.307-1.176-5.609,0.949-1.691
c-2.27-8.352-9.715-16.156-16.564-19.818c0.838,0.767,1.896,1.73,1.516,1.881c-3.406-2.028-2.807-2.186-3.295-3.043
c-2.775-1.129-2.957,0.091-4.795,0.002c-5.23-2.774-6.238-2.479-11.051-4.217l0.219,1.023c-3.465-1.154-4.037,0.438-7.782,0.004
c-0.228-0.178,1.2-0.644,2.375-0.815c-3.35,0.442-3.193-0.66-6.471,0.122c0.808-0.567,1.662-0.942,2.524-1.424
c-2.732,0.166-6.522,1.59-5.352,0.295c-4.456,1.988-12.37,4.779-16.811,8.943l-0.14-0.933c-2.035,2.443-8.874,7.296-9.419,10.46
l-0.544,0.127c-1.059,1.793-1.744,3.825-2.584,5.67c-1.385,2.36-2.03,0.908-1.833,1.278c-2.724,5.523-4.077,10.164-5.246,13.97
c0.833,1.245,0.02,7.495,0.335,12.497c-1.368,24.704,17.338,48.69,37.785,54.228c2.997,1.072,7.454,1.031,11.245,1.141
c-4.473-1.279-5.051-0.678-9.408-2.197c-3.143-1.48-3.832-3.17-6.058-5.102l0.881,1.557c-4.366-1.545-2.539-1.912-6.091-3.037
l0.941-1.229c-1.415-0.107-3.748-2.385-4.386-3.646l-1.548,0.061c-1.86-2.295-2.851-3.949-2.779-5.23l-0.5,0.891
c-0.567-0.973-6.843-8.607-3.587-6.83c-0.605-0.553-1.409-0.9-2.281-2.484l0.663-0.758c-1.567-2.016-2.884-4.6-2.784-5.461
c0.836,1.129,1.416,1.34,1.99,1.533c-3.957-9.818-4.179-0.541-7.176-9.994l0.634-0.051c-0.486-0.732-0.781-1.527-1.172-2.307
l0.276-2.75C4.667,58.121,6.719,47.409,7.13,41.534c0.285-2.389,2.378-4.932,3.97-8.92l-0.97-0.167
c1.854-3.234,10.586-12.988,14.63-12.486c1.959-2.461-0.389-0.009-0.772-0.629c4.303-4.453,5.656-3.146,8.56-3.947
c3.132-1.859-2.688,0.725-1.203-0.709c5.414-1.383,3.837-3.144,10.9-3.846c0.745,0.424-1.729,0.655-2.35,1.205
c4.511-2.207,14.275-1.705,20.617,1.225c7.359,3.439,15.627,13.605,15.953,23.17l0.371,0.1
c-0.188,3.802,0.582,8.199-0.752,12.238L76.992,46.856"/>
<path i:knockout="Off" fill="#A80030" d="M32.372,59.764l-0.252,1.26c1.181,1.604,2.118,3.342,3.626,4.596
C34.661,63.502,33.855,62.627,32.372,59.764"/>
<path i:knockout="Off" fill="#A80030" d="M35.164,59.654c-0.625-0.691-0.995-1.523-1.409-2.352
c0.396,1.457,1.207,2.709,1.962,3.982L35.164,59.654"/>
<path i:knockout="Off" fill="#A80030" d="M84.568,48.916l-0.264,0.662c-0.484,3.438-1.529,6.84-3.131,9.994
C82.943,56.244,84.088,52.604,84.568,48.916"/>
<path i:knockout="Off" fill="#A80030" d="M45.527,0.537C46.742,0.092,48.514,0.293,49.803,0c-1.68,0.141-3.352,0.225-5.003,0.438
L45.527,0.537"/>
<path i:knockout="Off" fill="#A80030" d="M2.872,23.219c0.28,2.592-1.95,3.598,0.494,1.889
C4.676,22.157,2.854,24.293,2.872,23.219"/>
<path i:knockout="Off" fill="#A80030" d="M0,35.215c0.563-1.728,0.665-2.766,0.88-3.766C-0.676,33.438,0.164,33.862,0,35.215"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,46 @@
<svg width="121" height="121" viewBox="0 0 121 121" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M62.8005 6.66987C62.0404 6.23103 61.1782 6 60.3005 6C59.4228 6 58.5606 6.23103 57.8005 6.66987L15.5005 31.0699C14.73 31.5146 14.0922 32.1568 13.6527 32.9302C13.2133 33.7037 12.9881 34.5804 13.0005 35.4699V84.0699C12.9881 84.9594 13.2133 85.8361 13.6527 86.6095C14.0922 87.383 14.73 88.0252 15.5005 88.4699L57.6005 112.77C58.3606 113.209 59.2228 113.44 60.1005 113.44C60.9782 113.44 61.8404 113.209 62.6005 112.77L104.8 88.4699C105.59 88.0381 106.248 87.4015 106.705 86.6272C107.163 85.8528 107.403 84.9693 107.4 84.0699V35.3699C107.403 34.4704 107.163 33.587 106.705 32.8126C106.248 32.0382 105.59 31.4016 104.8 30.9699L62.8005 6.66987Z" fill="url(#paint0_linear_1_2)"/>
<mask id="mask0_1_2" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="13" y="6" width="95" height="108">
<path fill-rule="evenodd" clip-rule="evenodd" d="M62.8005 6.66987C62.0404 6.23103 61.1782 6 60.3005 6C59.4228 6 58.5606 6.23103 57.8005 6.66987L15.5005 31.0699C14.73 31.5146 14.0922 32.1568 13.6527 32.9302C13.2133 33.7037 12.9881 34.5804 13.0005 35.4699V84.0699C12.9881 84.9594 13.2133 85.8361 13.6527 86.6095C14.0922 87.383 14.73 88.0252 15.5005 88.4699L57.6005 112.77C58.3606 113.209 59.2228 113.44 60.1005 113.44C60.9782 113.44 61.8404 113.209 62.6005 112.77L104.8 88.4699C105.59 88.0381 106.248 87.4015 106.705 86.6272C107.163 85.8528 107.403 84.9693 107.4 84.0699V35.3699C107.403 34.4704 107.163 33.587 106.705 32.8126C106.248 32.0382 105.59 31.4016 104.8 30.9699L62.8005 6.66987Z" fill="white"/>
</mask>
<g mask="url(#mask0_1_2)">
<path d="M105 31.0699L62.8005 6.66988C62.3926 6.44337 61.955 6.27507 61.5005 6.16988L14.0005 87.2699C14.4025 87.7396 14.8748 88.1444 15.4005 88.4699L57.8005 112.77C58.9849 113.441 60.3851 113.62 61.7005 113.27L106.2 31.8699C105.84 31.5209 105.437 31.2185 105 30.9699V31.0699Z" fill="url(#paint1_linear_1_2)"/>
<path d="M105 88.4699C105.607 88.127 106.14 87.6679 106.569 87.1188C106.998 86.5697 107.315 85.9414 107.5 85.2699L61.2005 6.06987C59.9674 5.82836 58.6886 6.04149 57.6005 6.66987L15.7005 30.8699L61.0005 113.47C61.6736 113.363 62.3191 113.125 62.9005 112.77L105 88.4699Z" fill="url(#paint2_linear_1_2)"/>
<path d="M105 88.4699L62.9005 112.77C62.3191 113.125 61.6736 113.363 61.0005 113.47L61.8005 114.97L108.7 87.8699V87.1699L107.5 85.1699C107.327 85.8596 107.016 86.5071 106.586 87.0741C106.157 87.641 105.617 88.1157 105 88.4699Z" fill="url(#paint3_linear_1_2)"/>
<path d="M105 88.4699L62.9005 112.77C62.3191 113.125 61.6736 113.363 61.0005 113.47L61.8005 114.97L108.7 87.8699V87.1699L107.5 85.1699C107.327 85.8596 107.016 86.5071 106.586 87.0741C106.157 87.641 105.617 88.1157 105 88.4699Z" fill="url(#paint4_linear_1_2)"/>
</g>
<defs>
<linearGradient id="paint0_linear_1_2" x1="77.4005" y1="24.7699" x2="39.3005" y2="102.47" gradientUnits="userSpaceOnUse">
<stop stop-color="#3F873F"/>
<stop offset="0.3" stop-color="#3F8B3D"/>
<stop offset="0.6" stop-color="#3E9637"/>
<stop offset="0.9" stop-color="#3DA92E"/>
<stop offset="1" stop-color="#3DAE2B"/>
</linearGradient>
<linearGradient id="paint1_linear_1_2" x1="53.9005" y1="65.3699" x2="160.7" y2="-13.5301" gradientUnits="userSpaceOnUse">
<stop offset="0.1" stop-color="#3F873F"/>
<stop offset="0.4" stop-color="#529F44"/>
<stop offset="0.7" stop-color="#63B649"/>
<stop offset="0.9" stop-color="#6ABF4B"/>
</linearGradient>
<linearGradient id="paint2_linear_1_2" x1="11.6005" y1="59.7699" x2="109" y2="59.7699" gradientUnits="userSpaceOnUse">
<stop offset="0.1" stop-color="#6ABF4B"/>
<stop offset="0.3" stop-color="#63B649"/>
<stop offset="0.6" stop-color="#529F44"/>
<stop offset="0.9" stop-color="#3F873F"/>
</linearGradient>
<linearGradient id="paint3_linear_1_2" x1="11.6005" y1="100.07" x2="109" y2="100.07" gradientUnits="userSpaceOnUse">
<stop offset="0.1" stop-color="#6ABF4B"/>
<stop offset="0.3" stop-color="#63B649"/>
<stop offset="0.6" stop-color="#529F44"/>
<stop offset="0.9" stop-color="#3F873F"/>
</linearGradient>
<linearGradient id="paint4_linear_1_2" x1="123.2" y1="22.4699" x2="63.1005" y2="145.07" gradientUnits="userSpaceOnUse">
<stop stop-color="#3F873F"/>
<stop offset="0.3" stop-color="#3F8B3D"/>
<stop offset="0.6" stop-color="#3E9637"/>
<stop offset="0.9" stop-color="#3DA92E"/>
<stop offset="1" stop-color="#3DAE2B"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB