authorgravatar for 69873514+sandersonstabo@users.noreply.github.comss <69873514+sandersonstabo@users.noreply.github.com> 2026-07-18 13:22:37+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-18 13:22:37+02:00
loge63403fe9259b67f8beccdbf8117671ffefaf48f
tree4e10a4b969eb7e9cc5a588c3aa7758c39a594832
parentc0e161af069ec501a44d72c4e59d38d3cf85706d
signature Signed by PGP key B5690EEEBB952194

[fix/feat:ui] Preserve open-in editor brand colors (#3225)

Co-authored-by: Julius Marminge <julius0216@outlook.com>

1 files changed, 42 insertions(+), 5 deletions(-)

apps/web/src/components/chat/OpenInPicker.tsx+42-5
......@@ -31,111 +31,138 @@ import {
3131 RustRoverIcon,
3232 WebStormIcon,
3333} from "../JetBrainsIcons";
34import { isMacPlatform, isWindowsPlatform } from "~/lib/utils";
34import { cn, isMacPlatform, isWindowsPlatform } from "~/lib/utils";
3535import { shellEnvironment } from "~/state/shell";
3636import { useAtomCommand } from "~/state/use-atom-command";
3737
38type OpenInOption = {
39 label: string;
40 Icon: Icon;
41 value: EditorId;
42 kind: "brand" | "generic";
43};
44
3845const resolveOptions = (platform: string, availableEditors: ReadonlyArray<EditorId>) => {
39 const baseOptions: ReadonlyArray<{ label: string; Icon: Icon; value: EditorId }> = [
46 const baseOptions: ReadonlyArray<OpenInOption> = [
4047 {
4148 label: "Cursor",
4249 Icon: CursorIcon,
4350 value: "cursor",
51 kind: "brand",
4452 },
4553 {
4654 label: "Trae",
4755 Icon: TraeIcon,
4856 value: "trae",
57 kind: "brand",
4958 },
5059 {
5160 label: "Kiro",
5261 Icon: KiroIcon,
5362 value: "kiro",
63 kind: "brand",
5464 },
5565 {
5666 label: "VS Code",
5767 Icon: VisualStudioCode,
5868 value: "vscode",
69 kind: "brand",
5970 },
6071 {
6172 label: "VS Code Insiders",
6273 Icon: VisualStudioCodeInsiders,
6374 value: "vscode-insiders",
75 kind: "brand",
6476 },
6577 {
6678 label: "VSCodium",
6779 Icon: VSCodium,
6880 value: "vscodium",
81 kind: "brand",
6982 },
7083 {
7184 label: "Zed",
7285 Icon: Zed,
7386 value: "zed",
87 kind: "brand",
7488 },
7589 {
7690 label: "Antigravity",
7791 Icon: AntigravityIcon,
7892 value: "antigravity",
93 kind: "brand",
7994 },
8095 {
8196 label: "IntelliJ IDEA",
8297 Icon: IntelliJIdeaIcon,
8398 value: "idea",
99 kind: "brand",
84100 },
85101 {
86102 label: "Aqua",
87103 Icon: AquaIcon,
88104 value: "aqua",
105 kind: "brand",
89106 },
90107 {
91108 label: "CLion",
92109 Icon: CLionIcon,
93110 value: "clion",
111 kind: "brand",
94112 },
95113 {
96114 label: "DataGrip",
97115 Icon: DataGripIcon,
98116 value: "datagrip",
117 kind: "brand",
99118 },
100119 {
101120 label: "DataSpell",
102121 Icon: DataSpellIcon,
103122 value: "dataspell",
123 kind: "brand",
104124 },
105125 {
106126 label: "GoLand",
107127 Icon: GoLandIcon,
108128 value: "goland",
129 kind: "brand",
109130 },
110131 {
111132 label: "PhpStorm",
112133 Icon: PhpStormIcon,
113134 value: "phpstorm",
135 kind: "brand",
114136 },
115137 {
116138 label: "PyCharm",
117139 Icon: PyCharmIcon,
118140 value: "pycharm",
141 kind: "brand",
119142 },
120143 {
121144 label: "Rider",
122145 Icon: RiderIcon,
123146 value: "rider",
147 kind: "brand",
124148 },
125149 {
126150 label: "RubyMine",
127151 Icon: RubyMineIcon,
128152 value: "rubymine",
153 kind: "brand",
129154 },
130155 {
131156 label: "RustRover",
132157 Icon: RustRoverIcon,
133158 value: "rustrover",
159 kind: "brand",
134160 },
135161 {
136162 label: "WebStorm",
137163 Icon: WebStormIcon,
138164 value: "webstorm",
165 kind: "brand",
139166 },
140167 {
141168 label: isMacPlatform(platform)
......@@ -145,12 +172,17 @@ const resolveOptions = (platform: string, availableEditors: ReadonlyArray<Editor
145172 : "Files",
146173 Icon: FolderClosedIcon,
147174 value: "file-manager",
175 kind: "generic",
148176 },
149177 ];
150178 const availableEditorSet = new Set(availableEditors);
151179 return baseOptions.filter((option) => availableEditorSet.has(option.value));
152180};
153181
182function getOpenInIconClass(kind: OpenInOption["kind"]) {
183 return cn(kind === "brand" ? "text-foreground opacity-100" : "text-muted-foreground");
184}
185
154186export const OpenInPicker = memo(function OpenInPicker({
155187 environmentId,
156188 keybindings,
......@@ -233,7 +265,12 @@ export const OpenInPicker = memo(function OpenInPicker({
233265 disabled={!preferredEditor || !openInCwd}
234266 onClick={() => openInEditor(preferredEditor)}
235267 >
236 {primaryOption?.Icon && <primaryOption.Icon aria-hidden="true" className="size-3.5" />}
268 {primaryOption?.Icon && (
269 <primaryOption.Icon
270 aria-hidden="true"
271 className={cn("size-3.5", getOpenInIconClass(primaryOption.kind))}
272 />
273 )}
237274 <span
238275 className={
239276 compact
......@@ -259,9 +296,9 @@ export const OpenInPicker = memo(function OpenInPicker({
259296 </MenuTrigger>
260297 <MenuPopup align="end">
261298 {options.length === 0 && <MenuItem disabled>No installed editors found</MenuItem>}
262 {options.map(({ label, Icon, value }) => (
299 {options.map(({ label, Icon, value, kind }) => (
263300 <MenuItem key={value} onClick={() => openInEditor(value)}>
264 <Icon aria-hidden="true" className="text-muted-foreground" />
301 <Icon aria-hidden="true" className={getOpenInIconClass(kind)} />
265302 {label}
266303 {value === preferredEditor && openFavoriteEditorShortcutLabel && (
267304 <MenuShortcut>{openFavoriteEditorShortcutLabel}</MenuShortcut>