Skip to content

Commit 0d4ad6b

Browse files
committed
feat: external icon settings
1 parent da7859c commit 0d4ad6b

File tree

22 files changed

+479
-39
lines changed

22 files changed

+479
-39
lines changed

examples/templates/aws-devcontainer/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
display_name: AWS EC2 (Devcontainer)
33
description: Provision AWS EC2 VMs with a devcontainer as Coder workspaces
4-
icon: ../../../site/static/icon/aws.png
4+
icon: ../../../site/static/icon/aws.svg
55
maintainer_github: coder
66
verified: true
77
tags: [vm, linux, aws, persistent, devcontainer]

examples/templates/aws-linux/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
display_name: AWS EC2 (Linux)
33
description: Provision AWS EC2 VMs as Coder workspaces
4-
icon: ../../../site/static/icon/aws.png
4+
icon: ../../../site/static/icon/aws.svg
55
maintainer_github: coder
66
verified: true
77
tags: [vm, linux, aws, persistent-vm]

examples/templates/aws-windows/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
display_name: AWS EC2 (Windows)
33
description: Provision AWS EC2 VMs as Coder workspaces
4-
icon: ../../../site/static/icon/aws.png
4+
icon: ../../../site/static/icon/aws.svg
55
maintainer_github: coder
66
verified: true
77
tags: [vm, windows, aws]

site/src/AppRouter.tsx

+1-7
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ import WorkspacesPage from "./pages/WorkspacesPage/WorkspacesPage";
2121
import UserSettingsLayout from "./pages/UserSettingsPage/Layout";
2222
import { TemplateSettingsLayout } from "./pages/TemplateSettingsPage/TemplateSettingsLayout";
2323
import { WorkspaceSettingsLayout } from "./pages/WorkspaceSettingsPage/WorkspaceSettingsLayout";
24-
import { ThemeOverride } from "contexts/ThemeProvider";
25-
import themes from "theme";
2624

2725
// Lazy load pages
2826
// - Pages that are secondary, not in the main navigation or not usually accessed
@@ -414,11 +412,7 @@ export const AppRouter: FC = () => {
414412
/>
415413
<Route
416414
path="/:username/:workspace/terminal"
417-
element={
418-
<ThemeOverride theme={themes.dark}>
419-
<TerminalPage />
420-
</ThemeOverride>
421-
}
415+
element={<TerminalPage />}
422416
/>
423417
<Route path="/cli-auth" element={<CliAuthenticationPage />} />
424418
<Route path="/icons" element={<IconsPage />} />

site/src/__mocks__/react-markdown.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { FC, PropsWithChildren } from "react";
22

3-
const ReactMarkdown: FC<PropsWithChildren<unknown>> = ({ children }) => {
3+
const ReactMarkdown: FC<PropsWithChildren> = ({ children }) => {
44
return <div data-testid="markdown">{children}</div>;
55
};
66

site/src/api/queries/users.ts

+1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export const me = (): UseQueryOptions<User> & {
131131
queryKey: meKey,
132132
initialData: initialUserData,
133133
queryFn: API.getAuthenticatedUser,
134+
refetchOnWindowFocus: true,
134135
};
135136
};
136137

site/src/components/RichParameterInput/RichParameterInput.stories.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export const Options: Story = {
9696
name: "Third option",
9797
value: "third_option",
9898
description: "",
99-
icon: "/icon/aws.png",
99+
icon: "/icon/aws.svg",
100100
},
101101
],
102102
}),
@@ -138,7 +138,7 @@ Very big.
138138
139139
> Wow, that description is straight up large. –Some guy, probably
140140
`,
141-
icon: "/icon/aws.png",
141+
icon: "/icon/aws.svg",
142142
},
143143
],
144144
}),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { chromatic } from "testHelpers/chromatic";
3+
import { IconsPage } from "./IconsPage";
4+
5+
const meta: Meta<typeof IconsPage> = {
6+
title: "pages/IconsPage",
7+
parameters: { chromatic },
8+
component: IconsPage,
9+
args: {},
10+
};
11+
12+
export default meta;
13+
type Story = StoryObj<typeof IconsPage>;
14+
15+
const Example: Story = {};
16+
17+
export { Example as IconsPage };

site/src/pages/IconsPage/IconsPage.tsx

+17-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import {
1919
} from "components/PageHeader/PageHeader";
2020
import { Stack } from "components/Stack/Stack";
2121
import icons from "theme/icons.json";
22+
import {
23+
defaultParametersForBuiltinIcons,
24+
parseImageParameters,
25+
} from "theme/externalImages";
2226
import { pageTitle } from "utils/page";
2327

2428
const iconsWithoutSuffix = icons.map((icon) => icon.split(".")[0]);
@@ -163,13 +167,19 @@ export const IconsPage: FC = () => {
163167
<img
164168
alt={icon.url}
165169
src={icon.url}
166-
css={{
167-
width: 60,
168-
height: 60,
169-
objectFit: "contain",
170-
pointerEvents: "none",
171-
padding: 12,
172-
}}
170+
css={[
171+
{
172+
width: 60,
173+
height: 60,
174+
objectFit: "contain",
175+
pointerEvents: "none",
176+
padding: 12,
177+
},
178+
parseImageParameters(
179+
theme.externalImages,
180+
defaultParametersForBuiltinIcons.get(icon.url) ?? "",
181+
),
182+
]}
173183
/>
174184
<figcaption
175185
css={{

site/src/pages/TerminalPage/TerminalPage.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import {
3333
PopoverContent,
3434
PopoverTrigger,
3535
} from "components/Popover/Popover";
36+
import { ThemeOverride } from "contexts/ThemeProvider";
37+
import themes from "theme";
3638

3739
export const Language = {
3840
workspaceErrorMessagePrefix: "Unable to fetch workspace: ",
@@ -293,7 +295,7 @@ const TerminalPage: FC = () => {
293295
]);
294296

295297
return (
296-
<>
298+
<ThemeOverride theme={themes.dark}>
297299
<Helmet>
298300
<title>
299301
{workspace.data
@@ -314,7 +316,7 @@ const TerminalPage: FC = () => {
314316
<BottomBar proxy={selectedProxy} latency={latency.latencyMS} />
315317
)}
316318
</div>
317-
</>
319+
</ThemeOverride>
318320
);
319321
};
320322

site/src/testHelpers/entities.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2244,7 +2244,7 @@ export const MockTemplateExample: TypesGen.TemplateExample = {
22442244
description: "Get started with Linux development on AWS ECS.",
22452245
markdown:
22462246
"\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",
2247-
icon: "/icon/aws.png",
2247+
icon: "/icon/aws.svg",
22482248
tags: ["aws", "cloud"],
22492249
};
22502250

@@ -2255,7 +2255,7 @@ export const MockTemplateExample2: TypesGen.TemplateExample = {
22552255
description: "Get started with Linux development on AWS EC2.",
22562256
markdown:
22572257
'\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',
2258-
icon: "/icon/aws.png",
2258+
icon: "/icon/aws.svg",
22592259
tags: ["aws", "cloud"],
22602260
};
22612261

site/src/theme/dark/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import experimental from "./experimental";
22
import monaco from "./monaco";
33
import muiTheme from "./mui";
4+
import { forDarkThemes } from "../externalImages";
45

56
export default {
67
...muiTheme,
78
experimental,
89
monaco,
10+
externalImages: forDarkThemes,
911
};

site/src/theme/darkBlue/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import experimental from "./experimental";
22
import monaco from "./monaco";
33
import muiTheme from "./mui";
4+
import { forDarkThemes } from "../externalImages";
45

56
export default {
67
...muiTheme,
78
experimental,
89
monaco,
10+
externalImages: forDarkThemes,
911
};

site/src/theme/externalImages.test.ts

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import {
2+
forDarkThemes,
3+
forLightThemes,
4+
getExternalImageStylesFromUrl,
5+
parseImageParameters,
6+
} from "./externalImages";
7+
8+
describe("externalImage parameters", () => {
9+
test("default parameters", () => {
10+
// Correctly selects default
11+
const widgetsStyles = getExternalImageStylesFromUrl(
12+
forDarkThemes,
13+
"/icon/widgets.svg",
14+
);
15+
expect(widgetsStyles).toBe(forDarkThemes.monochrome);
16+
17+
// Allows overrides
18+
const overrideStyles = getExternalImageStylesFromUrl(
19+
forDarkThemes,
20+
"/icon/widgets.svg?fullcolor",
21+
);
22+
expect(overrideStyles).toBe(forDarkThemes.fullcolor);
23+
24+
// Not actually a built-in
25+
const someoneElsesWidgetsStyles = getExternalImageStylesFromUrl(
26+
forDarkThemes,
27+
"https://example.com/icon/widgets.svg",
28+
);
29+
expect(someoneElsesWidgetsStyles).toBeUndefined();
30+
});
31+
32+
test("blackWithColor brightness", () => {
33+
const tryCase = (params: string) =>
34+
parseImageParameters(forDarkThemes, params);
35+
36+
const withDecimalValue = tryCase("?blackWithColor&brightness=1.5");
37+
expect(withDecimalValue?.filter).toBe(
38+
"invert(1) hue-rotate(180deg) brightness(1.5)",
39+
);
40+
41+
const withPercentageValue = tryCase("?blackWithColor&brightness=150%");
42+
expect(withPercentageValue?.filter).toBe(
43+
"invert(1) hue-rotate(180deg) brightness(150%)",
44+
);
45+
46+
// Sketchy `brightness` value will be ignored.
47+
const niceTry = tryCase(
48+
"?blackWithColor&brightness=</style><script>alert('leet hacking');</script>",
49+
);
50+
expect(niceTry?.filter).toBe("invert(1) hue-rotate(180deg)");
51+
52+
const withLightTheme = parseImageParameters(
53+
forLightThemes,
54+
"?blackWithColor&brightness=1.5",
55+
);
56+
expect(withLightTheme).toBeUndefined();
57+
});
58+
59+
test("whiteWithColor brightness", () => {
60+
const tryCase = (params: string) =>
61+
parseImageParameters(forLightThemes, params);
62+
63+
const withDecimalValue = tryCase("?whiteWithColor&brightness=1.5");
64+
expect(withDecimalValue?.filter).toBe(
65+
"invert(1) hue-rotate(180deg) brightness(1.5)",
66+
);
67+
68+
const withPercentageValue = tryCase("?whiteWithColor&brightness=150%");
69+
expect(withPercentageValue?.filter).toBe(
70+
"invert(1) hue-rotate(180deg) brightness(150%)",
71+
);
72+
73+
// Sketchy `brightness` value will be ignored.
74+
const niceTry = tryCase(
75+
"?whiteWithColor&brightness=</style><script>alert('leet hacking');</script>",
76+
);
77+
expect(niceTry?.filter).toBe("invert(1) hue-rotate(180deg)");
78+
79+
const withDarkTheme = parseImageParameters(
80+
forDarkThemes,
81+
"?whiteWithColor&brightness=1.5",
82+
);
83+
expect(withDarkTheme).toBeUndefined();
84+
});
85+
});

0 commit comments

Comments
 (0)